diff --git a/src/PrefixTable.cc b/src/PrefixTable.cc index 27f7c48c36..007e08349c 100644 --- a/src/PrefixTable.cc +++ b/src/PrefixTable.cc @@ -62,9 +62,9 @@ void* PrefixTable::Insert(const Val* value, void* data) } } -list PrefixTable::FindAll(const IPAddr& addr, int width) const +list> PrefixTable::FindAll(const IPAddr& addr, int width) const { - std::list out; + std::list> out; prefix_t* prefix = MakePrefix(addr, width); int elems = 0; @@ -73,14 +73,14 @@ list PrefixTable::FindAll(const IPAddr& addr, int width) const patricia_search_all(tree, prefix, &list, &elems); for ( int i = 0; i < elems; ++i ) - out.push_back(PrefixToIPPrefix(list[i]->prefix)); + out.push_back(std::make_tuple(PrefixToIPPrefix(list[i]->prefix), list[i]->data)); Deref_Prefix(prefix); free(list); return out; } -list PrefixTable::FindAll(const SubNetVal* value) const +list> PrefixTable::FindAll(const SubNetVal* value) const { return FindAll(value->AsSubNet().Prefix(), value->AsSubNet().LengthIPv6()); } diff --git a/src/PrefixTable.h b/src/PrefixTable.h index 8c329c93a9..6606b77e81 100644 --- a/src/PrefixTable.h +++ b/src/PrefixTable.h @@ -37,8 +37,8 @@ public: void* Lookup(const Val* value, bool exact = false) const; // Returns list of all found matches or empty list otherwise. - list FindAll(const IPAddr& addr, int width) const; - list FindAll(const SubNetVal* value) const; + list> FindAll(const IPAddr& addr, int width) const; + list> FindAll(const SubNetVal* value) const; // Returns pointer to data or nil if not found. void* Remove(const IPAddr& addr, int width); diff --git a/src/Val.cc b/src/Val.cc index 01a849c639..35233e9056 100644 --- a/src/Val.cc +++ b/src/Val.cc @@ -1833,6 +1833,54 @@ Val* TableVal::Lookup(Val* index, bool use_default_val) return def; } +VectorVal* TableVal::LookupSubnets(const SubNetVal* search) + { + if ( ! subnets ) + reporter->InternalError("LookupSubnets called on wrong table type"); + + VectorVal* result = new VectorVal(internal_type("subnet_vec")->AsVectorType()); + + auto matches = subnets->FindAll(search); + for ( auto element : matches ) + { + SubNetVal* s = new SubNetVal(get<0>(element)); + result->Assign(result->Size(), s); + } + + return result; + } + +TableVal* TableVal::LookupSubnetValues(const SubNetVal* search) + { + if ( ! subnets ) + reporter->InternalError("LookupSubnetValues called on wrong table type"); + + TableVal* nt = new TableVal(this->Type()->Ref()->AsTableType()); + + auto matches = subnets->FindAll(search); + for ( auto element : matches ) + { + SubNetVal* s = new SubNetVal(get<0>(element)); + TableEntryVal* entry = reinterpret_cast(get<1>(element)); + + if ( entry && entry->Value() ) + nt->Assign(s, entry->Value()->Ref()); + else + nt->Assign(s, 0); // set + + if ( entry ) + { + entry->SetExpireAccess(network_time); + if ( LoggingAccess() && attrs->FindAttr(ATTR_EXPIRE_READ) ) + ReadOperation(s, entry); + } + + Unref(s); // assign does not consume index + } + + return nt; + } + bool TableVal::UpdateTimestamp(Val* index) { TableEntryVal* v; @@ -1854,7 +1902,7 @@ bool TableVal::UpdateTimestamp(Val* index) return false; v->SetExpireAccess(network_time); - if ( attrs->FindAttr(ATTR_EXPIRE_READ) ) + if ( LoggingAccess() && attrs->FindAttr(ATTR_EXPIRE_READ) ) ReadOperation(index, v); return true; diff --git a/src/Val.h b/src/Val.h index fdc60436bf..a49a2e2235 100644 --- a/src/Val.h +++ b/src/Val.h @@ -790,6 +790,16 @@ public: // need to Ref/Unref it when calling the default function. Val* Lookup(Val* index, bool use_default_val = true); + // For a table[subnet]/set[subnet], return all subnets that cover + // the given subnet. + // Causes an internal error if called for any other kind of table. + VectorVal* LookupSubnets(const SubNetVal* s); + + // For a set[subnet]/table[subnet], return a new table that only contains + // entries that cover the given subnet. + // Causes an internal error if called for any other kind of table. + TableVal* LookupSubnetValues(const SubNetVal* s); + // Sets the timestamp for the given index to network time. // Returns false if index does not exist. bool UpdateTimestamp(Val* index); diff --git a/src/bro.bif b/src/bro.bif index 38f588d675..2c55c2bc95 100644 --- a/src/bro.bif +++ b/src/bro.bif @@ -1031,7 +1031,7 @@ function clear_table%(v: any%): any return 0; %} -## Gets all subnets that match a given subnet from a set/table[subnet] +## Gets all subnets that contain a given subnet from a set/table[subnet] ## ## search: the subnet to search for. ## @@ -1046,23 +1046,26 @@ function matching_subnets%(search: subnet, t: any%): subnet_vec return nullptr; } - const PrefixTable* pt = t->AsTableVal()->Subnets(); - if ( ! pt ) + return t->AsTableVal()->LookupSubnets(search); + %} + +## For a set[subnet]/table[subnet], create a new table that contains all entries that +## contain a given subnet. +## +## search: the subnet to search for. +## +## t: the set[subnet] or table[subnet]. +## +## Returns: A new table that contains all the entries that cover the subnet searched for. +function filter_subnet_table%(search: subnet, t: any%): any + %{ + if ( t->Type()->Tag() != TYPE_TABLE || ! t->Type()->AsTableType()->IsSubNetIndex() ) { - reporter->Error("matching_subnets encountered nonexisting prefix table."); + reporter->Error("filter_subnet_table needs to be called on a set[subnet]/table[subnet]."); return nullptr; } - VectorVal* result_v = new VectorVal(internal_type("subnet_vec")->AsVectorType()); - - auto matches = pt->FindAll(search); - for ( auto element : matches ) - { - SubNetVal* s = new SubNetVal(element); - result_v->Assign(result_v->Size(), s); - } - - return result_v; + return t->AsTableVal()->LookupSubnetValues(search); %} ## Checks if a specific subnet is a member of a set/table[subnet]. @@ -1078,14 +1081,14 @@ function check_subnet%(search: subnet, t: any%): bool %{ if ( t->Type()->Tag() != TYPE_TABLE || ! t->Type()->AsTableType()->IsSubNetIndex() ) { - reporter->Error("matching_subnets needs to be called on a set[subnet]/table[subnet]."); + reporter->Error("check_subnet needs to be called on a set[subnet]/table[subnet]."); return nullptr; } const PrefixTable* pt = t->AsTableVal()->Subnets(); if ( ! pt ) { - reporter->Error("matching_subnets encountered nonexisting prefix table."); + reporter->Error("check_subnet encountered nonexisting prefix table."); return nullptr; } diff --git a/testing/btest/Baseline/bifs.filter_subnet_table/output b/testing/btest/Baseline/bifs.filter_subnet_table/output new file mode 100644 index 0000000000..d86ca621a5 --- /dev/null +++ b/testing/btest/Baseline/bifs.filter_subnet_table/output @@ -0,0 +1,20 @@ +{ +10.0.0.0/8, +10.2.0.2/31, +10.2.0.0/16 +} +{ +[10.0.0.0/8] = a, +[10.2.0.2/31] = c, +[10.2.0.0/16] = b +} +{ +[10.0.0.0/8] = a, +[10.3.0.0/16] = e +} +{ + +} +{ + +} diff --git a/testing/btest/bifs/filter_subnet_table.bro b/testing/btest/bifs/filter_subnet_table.bro new file mode 100644 index 0000000000..7659096a71 --- /dev/null +++ b/testing/btest/bifs/filter_subnet_table.bro @@ -0,0 +1,49 @@ +# @TEST-EXEC: bro -b %INPUT >output +# @TEST-EXEC: btest-diff output + +global testa: set[subnet] = { + 10.0.0.0/8, + 10.2.0.0/16, + 10.2.0.2/31, + 10.1.0.0/16, + 10.3.0.0/16, + 5.0.0.0/8, + 5.5.0.0/25, + 5.2.0.0/32, + 7.2.0.0/32, + [2607:f8b0:4008:807::200e]/64, + [2607:f8b0:4007:807::200e]/64, + [2607:f8b0:4007:807::200e]/128 +}; + +global testb: table[subnet] of string = { + [10.0.0.0/8] = "a", + [10.2.0.0/16] = "b", + [10.2.0.2/31] = "c", + [10.1.0.0/16] = "d", + [10.3.0.0/16] = "e", + [5.0.0.0/8] = "f", + [5.5.0.0/25] = "g", + [5.2.0.0/32] = "h", + [7.2.0.0/32] = "i", + [[2607:f8b0:4008:807::200e]/64] = "j", + [[2607:f8b0:4007:807::200e]/64] = "k", + [[2607:f8b0:4007:807::200e]/128] = "l" +}; + + +event bro_init() + { + local c = filter_subnet_table(10.2.0.2/32, testa); + print c; + c = filter_subnet_table(10.2.0.2/32, testb); + print c; + c = filter_subnet_table(10.3.0.2/32, testb); + print c; + c = filter_subnet_table(1.0.0.0/8, testb); + print c; + + local unspecified: table[subnet] of string = table(); + c = filter_subnet_table(10.2.0.2/32, unspecified); + print c; + }