mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
Add bif that allows searching for all matching subnets in table.
Example: global test: set[subnet] = { 10.0.0.0/8, 10.1.0.0/16, 10.2.0.0/16, 10.2.0.2/31 } print matching_subnets(10.2.0.2/32, test); -> [10.2.0.2/31, 10.2.0.0/16, 10.0.0.0/8]
This commit is contained in:
parent
42e4072673
commit
562e5a9f63
10 changed files with 274 additions and 11 deletions
|
@ -263,7 +263,7 @@ global plugin_counter: count = 1;
|
||||||
global plugins: vector of PluginState;
|
global plugins: vector of PluginState;
|
||||||
global plugin_ids: table[count] of PluginState;
|
global plugin_ids: table[count] of PluginState;
|
||||||
|
|
||||||
# These tables hold informations about rules _after_ they have been
|
# These tables hold information about rules _after_ they have been
|
||||||
# succesfully added. Currently no information about the rules is held
|
# succesfully added. Currently no information about the rules is held
|
||||||
# in these tables while they are in the process of being added.
|
# in these tables while they are in the process of being added.
|
||||||
global rules: table[string,count] of Rule; # Rules indexed by id and cid
|
global rules: table[string,count] of Rule; # Rules indexed by id and cid
|
||||||
|
|
|
@ -39,6 +39,13 @@ type count_set: set[count];
|
||||||
## directly and then remove this alias.
|
## directly and then remove this alias.
|
||||||
type index_vec: vector of count;
|
type index_vec: vector of count;
|
||||||
|
|
||||||
|
## A vector of subnets.
|
||||||
|
##
|
||||||
|
## .. todo:: We need this type definition only for declaring builtin functions
|
||||||
|
## via ``bifcl``. We should extend ``bifcl`` to understand composite types
|
||||||
|
## directly and then remove this alias.
|
||||||
|
type subnet_vec: vector of subnet;
|
||||||
|
|
||||||
## A vector of any, used by some builtin functions to store a list of varying
|
## A vector of any, used by some builtin functions to store a list of varying
|
||||||
## types.
|
## types.
|
||||||
##
|
##
|
||||||
|
|
|
@ -1,21 +1,48 @@
|
||||||
#include "PrefixTable.h"
|
#include "PrefixTable.h"
|
||||||
#include "Reporter.h"
|
#include "Reporter.h"
|
||||||
|
|
||||||
inline static prefix_t* make_prefix(const IPAddr& addr, int width)
|
prefix_t* PrefixTable::MakePrefix(const IPAddr& addr, int width)
|
||||||
{
|
{
|
||||||
prefix_t* prefix = (prefix_t*) safe_malloc(sizeof(prefix_t));
|
prefix_t* prefix = (prefix_t*) safe_malloc(sizeof(prefix_t));
|
||||||
|
|
||||||
|
if ( addr.GetFamily() == IPv4 )
|
||||||
|
{
|
||||||
|
addr.CopyIPv4(&prefix->add.sin);
|
||||||
|
prefix->family = AF_INET;
|
||||||
|
prefix->bitlen = width;
|
||||||
|
prefix->ref_count = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
addr.CopyIPv6(&prefix->add.sin6);
|
addr.CopyIPv6(&prefix->add.sin6);
|
||||||
prefix->family = AF_INET6;
|
prefix->family = AF_INET6;
|
||||||
prefix->bitlen = width;
|
prefix->bitlen = width;
|
||||||
prefix->ref_count = 1;
|
prefix->ref_count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
return prefix;
|
return prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IPPrefix PrefixTable::PrefixToIPPrefix(prefix_t* prefix)
|
||||||
|
{
|
||||||
|
if ( prefix->family == AF_INET )
|
||||||
|
{
|
||||||
|
return IPPrefix(IPAddr(IPv4, reinterpret_cast<const uint32_t*>(&prefix->add.sin), IPAddr::Network), prefix->bitlen);
|
||||||
|
}
|
||||||
|
else if ( prefix->family == AF_INET6 )
|
||||||
|
{
|
||||||
|
return IPPrefix(IPAddr(IPv6, reinterpret_cast<const uint32_t*>(&prefix->add.sin6), IPAddr::Network), prefix->bitlen, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reporter->InternalWarning("Unknown prefix family for PrefixToIPAddr");
|
||||||
|
return IPPrefix();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void* PrefixTable::Insert(const IPAddr& addr, int width, void* data)
|
void* PrefixTable::Insert(const IPAddr& addr, int width, void* data)
|
||||||
{
|
{
|
||||||
prefix_t* prefix = make_prefix(addr, width);
|
prefix_t* prefix = MakePrefix(addr, width);
|
||||||
patricia_node_t* node = patricia_lookup(tree, prefix);
|
patricia_node_t* node = patricia_lookup(tree, prefix);
|
||||||
Deref_Prefix(prefix);
|
Deref_Prefix(prefix);
|
||||||
|
|
||||||
|
@ -43,12 +70,12 @@ void* PrefixTable::Insert(const Val* value, void* data)
|
||||||
|
|
||||||
switch ( value->Type()->Tag() ) {
|
switch ( value->Type()->Tag() ) {
|
||||||
case TYPE_ADDR:
|
case TYPE_ADDR:
|
||||||
return Insert(value->AsAddr(), 128, data);
|
return Insert(value->AsAddr(), value->AsAddr().GetFamily() == IPv4 ? 32 : 128, data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TYPE_SUBNET:
|
case TYPE_SUBNET:
|
||||||
return Insert(value->AsSubNet().Prefix(),
|
return Insert(value->AsSubNet().Prefix(),
|
||||||
value->AsSubNet().LengthIPv6(), data);
|
value->AsSubNet().Length(), data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -57,13 +84,41 @@ void* PrefixTable::Insert(const Val* value, void* data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
list<IPPrefix> PrefixTable::FindAll(const IPAddr& addr, int width) const
|
||||||
|
{
|
||||||
|
std::list<IPPrefix> out;
|
||||||
|
prefix_t* prefix = MakePrefix(addr, width);
|
||||||
|
|
||||||
|
int elems = 0;
|
||||||
|
patricia_node_t** list = nullptr;
|
||||||
|
|
||||||
|
patricia_search_all(tree, prefix, &list, &elems);
|
||||||
|
|
||||||
|
for ( int i = 0; i < elems; ++i )
|
||||||
|
{
|
||||||
|
out.push_back(PrefixToIPPrefix(list[i]->prefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
Deref_Prefix(prefix);
|
||||||
|
free(list);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
list<IPPrefix> PrefixTable::FindAll(const SubNetVal* value) const
|
||||||
|
{
|
||||||
|
return FindAll(value->AsSubNet().Prefix(), value->AsSubNet().LengthIPv6());
|
||||||
|
}
|
||||||
|
|
||||||
void* PrefixTable::Lookup(const IPAddr& addr, int width, bool exact) const
|
void* PrefixTable::Lookup(const IPAddr& addr, int width, bool exact) const
|
||||||
{
|
{
|
||||||
prefix_t* prefix = make_prefix(addr, width);
|
prefix_t* prefix = MakePrefix(addr, width);
|
||||||
patricia_node_t* node =
|
patricia_node_t* node =
|
||||||
exact ? patricia_search_exact(tree, prefix) :
|
exact ? patricia_search_exact(tree, prefix) :
|
||||||
patricia_search_best(tree, prefix);
|
patricia_search_best(tree, prefix);
|
||||||
|
|
||||||
|
int elems = 0;
|
||||||
|
patricia_node_t** list = nullptr;
|
||||||
|
|
||||||
Deref_Prefix(prefix);
|
Deref_Prefix(prefix);
|
||||||
return node ? node->data : 0;
|
return node ? node->data : 0;
|
||||||
}
|
}
|
||||||
|
@ -94,7 +149,7 @@ void* PrefixTable::Lookup(const Val* value, bool exact) const
|
||||||
|
|
||||||
void* PrefixTable::Remove(const IPAddr& addr, int width)
|
void* PrefixTable::Remove(const IPAddr& addr, int width)
|
||||||
{
|
{
|
||||||
prefix_t* prefix = make_prefix(addr, width);
|
prefix_t* prefix = MakePrefix(addr, width);
|
||||||
patricia_node_t* node = patricia_search_exact(tree, prefix);
|
patricia_node_t* node = patricia_search_exact(tree, prefix);
|
||||||
Deref_Prefix(prefix);
|
Deref_Prefix(prefix);
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,10 @@ public:
|
||||||
void* Lookup(const IPAddr& addr, int width, bool exact = false) const;
|
void* Lookup(const IPAddr& addr, int width, bool exact = false) const;
|
||||||
void* Lookup(const Val* value, bool exact = false) const;
|
void* Lookup(const Val* value, bool exact = false) const;
|
||||||
|
|
||||||
|
// Returns list of all found matches or empty list otherwhise.
|
||||||
|
list<IPPrefix> FindAll(const IPAddr& addr, int width) const;
|
||||||
|
list<IPPrefix> FindAll(const SubNetVal* value) const;
|
||||||
|
|
||||||
// Returns pointer to data or nil if not found.
|
// Returns pointer to data or nil if not found.
|
||||||
void* Remove(const IPAddr& addr, int width);
|
void* Remove(const IPAddr& addr, int width);
|
||||||
void* Remove(const Val* value);
|
void* Remove(const Val* value);
|
||||||
|
@ -45,6 +49,10 @@ public:
|
||||||
iterator InitIterator();
|
iterator InitIterator();
|
||||||
void* GetNext(iterator* i);
|
void* GetNext(iterator* i);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static prefix_t* MakePrefix(const IPAddr& addr, int width);
|
||||||
|
static IPPrefix PrefixToIPPrefix(prefix_t* p);
|
||||||
|
|
||||||
patricia_tree_t* tree;
|
patricia_tree_t* tree;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -814,6 +814,11 @@ public:
|
||||||
int Size() const { return AsTable()->Length(); }
|
int Size() const { return AsTable()->Length(); }
|
||||||
int RecursiveSize() const;
|
int RecursiveSize() const;
|
||||||
|
|
||||||
|
// Returns the Prefix table used inside the table (if present).
|
||||||
|
// This allows us to do more direct queries to this specialized
|
||||||
|
// type that the general Table API does not allow.
|
||||||
|
const PrefixTable* Subnets() const { return subnets; }
|
||||||
|
|
||||||
void Describe(ODesc* d) const override;
|
void Describe(ODesc* d) const override;
|
||||||
|
|
||||||
void InitTimer(double delay);
|
void InitTimer(double delay);
|
||||||
|
|
34
src/bro.bif
34
src/bro.bif
|
@ -1031,6 +1031,40 @@ function clear_table%(v: any%): any
|
||||||
return 0;
|
return 0;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
## Gets all subnets that match a given subnet from a set/table[subnet]
|
||||||
|
##
|
||||||
|
## search: the subnet to search for.
|
||||||
|
##
|
||||||
|
## t: the set[subnet] or table[subnet].
|
||||||
|
##
|
||||||
|
## Returns: All the keys of the set or table that cover the subnet searched for.
|
||||||
|
function matching_subnets%(search: subnet, t: any%): subnet_vec
|
||||||
|
%{
|
||||||
|
if ( t->Type()->Tag() != TYPE_TABLE || ! t->Type()->AsTableType()->IsSubNetIndex() )
|
||||||
|
{
|
||||||
|
reporter->Error("matching_subnets 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.");
|
||||||
|
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;
|
||||||
|
%}
|
||||||
|
|
||||||
## Checks whether two objects reference the same internal object. This function
|
## Checks whether two objects reference the same internal object. This function
|
||||||
## uses equality comparison of C++ raw pointer values to determine if the two
|
## uses equality comparison of C++ raw pointer values to determine if the two
|
||||||
## objects are the same.
|
## objects are the same.
|
||||||
|
|
105
src/patricia.c
105
src/patricia.c
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Johanna Amann <johanna@icir.org>
|
||||||
|
*
|
||||||
|
* Added patricia_search_all function.
|
||||||
|
*/
|
||||||
/*
|
/*
|
||||||
* Dave Plonka <plonka@doit.wisc.edu>
|
* Dave Plonka <plonka@doit.wisc.edu>
|
||||||
*
|
*
|
||||||
|
@ -61,6 +66,7 @@ static char copyright[] =
|
||||||
#include <string.h> /* memcpy, strchr, strlen */
|
#include <string.h> /* memcpy, strchr, strlen */
|
||||||
#include <arpa/inet.h> /* for inet_addr */
|
#include <arpa/inet.h> /* for inet_addr */
|
||||||
#include <sys/types.h> /* for u_short, etc. */
|
#include <sys/types.h> /* for u_short, etc. */
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "patricia.h"
|
#include "patricia.h"
|
||||||
|
|
||||||
|
@ -561,6 +567,105 @@ patricia_search_exact (patricia_tree_t *patricia, prefix_t *prefix)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
patricia_search_all (patricia_tree_t *patricia, prefix_t *prefix, patricia_node_t ***list, int *n)
|
||||||
|
{
|
||||||
|
patricia_node_t *node;
|
||||||
|
patricia_node_t *stack[PATRICIA_MAXBITS + 1];
|
||||||
|
u_char *addr;
|
||||||
|
u_int bitlen;
|
||||||
|
int cnt = 0;
|
||||||
|
|
||||||
|
assert (patricia);
|
||||||
|
assert (prefix);
|
||||||
|
assert (prefix->bitlen <= patricia->maxbits);
|
||||||
|
assert (n);
|
||||||
|
assert (list);
|
||||||
|
assert (*list == NULL);
|
||||||
|
|
||||||
|
*n = 0;
|
||||||
|
|
||||||
|
if (patricia->head == NULL)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
node = patricia->head;
|
||||||
|
addr = prefix_touchar (prefix);
|
||||||
|
bitlen = prefix->bitlen;
|
||||||
|
|
||||||
|
while (node->bit < bitlen) {
|
||||||
|
|
||||||
|
if (node->prefix) {
|
||||||
|
#ifdef PATRICIA_DEBUG
|
||||||
|
fprintf (stderr, "patricia_search_all: push %s/%d\n",
|
||||||
|
prefix_toa (node->prefix), node->prefix->bitlen);
|
||||||
|
#endif /* PATRICIA_DEBUG */
|
||||||
|
stack[cnt++] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BIT_TEST (addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) {
|
||||||
|
#ifdef PATRICIA_DEBUG
|
||||||
|
if (node->prefix)
|
||||||
|
fprintf (stderr, "patricia_search_all: take right %s/%d\n",
|
||||||
|
prefix_toa (node->prefix), node->prefix->bitlen);
|
||||||
|
else
|
||||||
|
fprintf (stderr, "patricia_search_all: take right at %d\n",
|
||||||
|
node->bit);
|
||||||
|
#endif /* PATRICIA_DEBUG */
|
||||||
|
node = node->r;
|
||||||
|
} else {
|
||||||
|
#ifdef PATRICIA_DEBUG
|
||||||
|
if (node->prefix)
|
||||||
|
fprintf (stderr, "patricia_search_all: take left %s/%d\n",
|
||||||
|
prefix_toa (node->prefix), node->prefix->bitlen);
|
||||||
|
else
|
||||||
|
fprintf (stderr, "patricia_search_all: take left at %d\n",
|
||||||
|
node->bit);
|
||||||
|
#endif /* PATRICIA_DEBUG */
|
||||||
|
node = node->l;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node == NULL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node && node->prefix)
|
||||||
|
stack[cnt++] = node;
|
||||||
|
|
||||||
|
#ifdef PATRICIA_DEBUG
|
||||||
|
if (node == NULL)
|
||||||
|
fprintf (stderr, "patricia_search_all: stop at null\n");
|
||||||
|
else if (node->prefix)
|
||||||
|
fprintf (stderr, "patricia_search_all: stop at %s/%d\n",
|
||||||
|
prefix_toa (node->prefix), node->prefix->bitlen);
|
||||||
|
else
|
||||||
|
fprintf (stderr, "patricia_search_all: stop at %d\n", node->bit);
|
||||||
|
#endif /* PATRICIA_DEBUG */
|
||||||
|
|
||||||
|
if (cnt <= 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// ok, now we have an upper bound of how much we can return. Let's just alloc that...
|
||||||
|
patricia_node_t **outlist = calloc(cnt, sizeof(patricia_node_t*));
|
||||||
|
|
||||||
|
while (--cnt >= 0) {
|
||||||
|
node = stack[cnt];
|
||||||
|
#ifdef PATRICIA_DEBUG
|
||||||
|
fprintf (stderr, "patricia_search_all: pop %s/%d\n",
|
||||||
|
prefix_toa (node->prefix), node->prefix->bitlen);
|
||||||
|
#endif /* PATRICIA_DEBUG */
|
||||||
|
if (comp_with_mask (prefix_tochar (node->prefix), prefix_tochar (prefix), node->prefix->bitlen)) {
|
||||||
|
#ifdef PATRICIA_DEBUG
|
||||||
|
fprintf (stderr, "patricia_search_all: found %s/%d\n",
|
||||||
|
prefix_toa (node->prefix), node->prefix->bitlen);
|
||||||
|
#endif /* PATRICIA_DEBUG */
|
||||||
|
outlist[*n] = node;
|
||||||
|
(*n)++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*list = outlist;
|
||||||
|
return (*n == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* if inclusive != 0, "best" may be the given prefix itself */
|
/* if inclusive != 0, "best" may be the given prefix itself */
|
||||||
patricia_node_t *
|
patricia_node_t *
|
||||||
|
|
|
@ -104,6 +104,7 @@ typedef struct _patricia_tree_t {
|
||||||
|
|
||||||
|
|
||||||
patricia_node_t *patricia_search_exact (patricia_tree_t *patricia, prefix_t *prefix);
|
patricia_node_t *patricia_search_exact (patricia_tree_t *patricia, prefix_t *prefix);
|
||||||
|
bool patricia_search_all (patricia_tree_t *patricia, prefix_t *prefix, patricia_node_t ***list, int *n);
|
||||||
patricia_node_t *patricia_search_best (patricia_tree_t *patricia, prefix_t *prefix);
|
patricia_node_t *patricia_search_best (patricia_tree_t *patricia, prefix_t *prefix);
|
||||||
patricia_node_t * patricia_search_best2 (patricia_tree_t *patricia, prefix_t *prefix,
|
patricia_node_t * patricia_search_best2 (patricia_tree_t *patricia, prefix_t *prefix,
|
||||||
int inclusive);
|
int inclusive);
|
||||||
|
|
18
testing/btest/Baseline/bifs.matching_subnets/output
Normal file
18
testing/btest/Baseline/bifs.matching_subnets/output
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
10.0.0.0/8,
|
||||||
|
10.3.0.0/16,
|
||||||
|
10.2.0.2/31,
|
||||||
|
2607:f8b0:4007:807::/64,
|
||||||
|
10.2.0.0/16,
|
||||||
|
5.2.0.0/32,
|
||||||
|
5.5.0.0/25,
|
||||||
|
10.1.0.0/16,
|
||||||
|
5.0.0.0/8,
|
||||||
|
2607:f8b0:4007:807::200e/128,
|
||||||
|
7.2.0.0/32,
|
||||||
|
2607:f8b0:4008:807::/64
|
||||||
|
}
|
||||||
|
[10.2.0.2/31, 10.2.0.0/16, 10.0.0.0/8]
|
||||||
|
[2607:f8b0:4007:807::200e/128, 2607:f8b0:4007:807::/64]
|
||||||
|
[]
|
||||||
|
[10.0.0.0/8]
|
30
testing/btest/bifs/matching_subnets.bro
Normal file
30
testing/btest/bifs/matching_subnets.bro
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# @TEST-EXEC: bro -b %INPUT >output
|
||||||
|
# @TEST-EXEC: btest-diff output
|
||||||
|
|
||||||
|
global testt: 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
|
||||||
|
};
|
||||||
|
|
||||||
|
event bro_init()
|
||||||
|
{
|
||||||
|
print testt;
|
||||||
|
local c = matching_subnets(10.2.0.2/32, testt);
|
||||||
|
print c;
|
||||||
|
c = matching_subnets([2607:f8b0:4007:807::200e]/128, testt);
|
||||||
|
print c;
|
||||||
|
c = matching_subnets(128.0.0.1/32, testt);
|
||||||
|
print c;
|
||||||
|
c = matching_subnets(10.0.0.2/8, testt);
|
||||||
|
print c;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue