diff --git a/scripts/base/frameworks/netcontrol/main.bro b/scripts/base/frameworks/netcontrol/main.bro index e950e0d163..c3ad4c0ff3 100644 --- a/scripts/base/frameworks/netcontrol/main.bro +++ b/scripts/base/frameworks/netcontrol/main.bro @@ -263,7 +263,7 @@ global plugin_counter: count = 1; global plugins: vector 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 # 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 diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index 1df4399922..9c60d76746 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -39,6 +39,13 @@ type count_set: set[count]; ## directly and then remove this alias. 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 ## types. ## diff --git a/src/PrefixTable.cc b/src/PrefixTable.cc index d31203c9d3..d2d5b286a5 100644 --- a/src/PrefixTable.cc +++ b/src/PrefixTable.cc @@ -1,21 +1,48 @@ #include "PrefixTable.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)); - addr.CopyIPv6(&prefix->add.sin6); - prefix->family = AF_INET6; - prefix->bitlen = width; - prefix->ref_count = 1; + 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); + prefix->family = AF_INET6; + prefix->bitlen = width; + prefix->ref_count = 1; + } return prefix; } +IPPrefix PrefixTable::PrefixToIPPrefix(prefix_t* prefix) + { + if ( prefix->family == AF_INET ) + { + return IPPrefix(IPAddr(IPv4, reinterpret_cast(&prefix->add.sin), IPAddr::Network), prefix->bitlen); + } + else if ( prefix->family == AF_INET6 ) + { + return IPPrefix(IPAddr(IPv6, reinterpret_cast(&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) { - prefix_t* prefix = make_prefix(addr, width); + prefix_t* prefix = MakePrefix(addr, width); patricia_node_t* node = patricia_lookup(tree, prefix); Deref_Prefix(prefix); @@ -43,12 +70,12 @@ void* PrefixTable::Insert(const Val* value, void* data) switch ( value->Type()->Tag() ) { case TYPE_ADDR: - return Insert(value->AsAddr(), 128, data); + return Insert(value->AsAddr(), value->AsAddr().GetFamily() == IPv4 ? 32 : 128, data); break; case TYPE_SUBNET: return Insert(value->AsSubNet().Prefix(), - value->AsSubNet().LengthIPv6(), data); + value->AsSubNet().Length(), data); break; default: @@ -57,13 +84,41 @@ void* PrefixTable::Insert(const Val* value, void* data) } } +list PrefixTable::FindAll(const IPAddr& addr, int width) const + { + std::list 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 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 { - prefix_t* prefix = make_prefix(addr, width); + prefix_t* prefix = MakePrefix(addr, width); patricia_node_t* node = exact ? patricia_search_exact(tree, prefix) : patricia_search_best(tree, prefix); + int elems = 0; + patricia_node_t** list = nullptr; + Deref_Prefix(prefix); 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) { - prefix_t* prefix = make_prefix(addr, width); + prefix_t* prefix = MakePrefix(addr, width); patricia_node_t* node = patricia_search_exact(tree, prefix); Deref_Prefix(prefix); diff --git a/src/PrefixTable.h b/src/PrefixTable.h index 2e5f43a0a8..626ce0b553 100644 --- a/src/PrefixTable.h +++ b/src/PrefixTable.h @@ -36,6 +36,10 @@ public: void* Lookup(const IPAddr& addr, int width, bool exact = false) const; void* Lookup(const Val* value, bool exact = false) const; + // Returns list of all found matches or empty list otherwhise. + 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); void* Remove(const Val* value); @@ -45,6 +49,10 @@ public: iterator InitIterator(); void* GetNext(iterator* i); +private: + static prefix_t* MakePrefix(const IPAddr& addr, int width); + static IPPrefix PrefixToIPPrefix(prefix_t* p); + patricia_tree_t* tree; }; diff --git a/src/Val.h b/src/Val.h index 86d75af94a..fdc60436bf 100644 --- a/src/Val.h +++ b/src/Val.h @@ -814,6 +814,11 @@ public: int Size() const { return AsTable()->Length(); } 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 InitTimer(double delay); diff --git a/src/bro.bif b/src/bro.bif index 72e7067b92..793efc5eba 100644 --- a/src/bro.bif +++ b/src/bro.bif @@ -1031,6 +1031,40 @@ function clear_table%(v: any%): any 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 ## uses equality comparison of C++ raw pointer values to determine if the two ## objects are the same. diff --git a/src/patricia.c b/src/patricia.c index c4815b40ec..552019be09 100644 --- a/src/patricia.c +++ b/src/patricia.c @@ -1,3 +1,8 @@ +/* + * Johanna Amann + * + * Added patricia_search_all function. + */ /* * Dave Plonka * @@ -61,6 +66,7 @@ static char copyright[] = #include /* memcpy, strchr, strlen */ #include /* for inet_addr */ #include /* for u_short, etc. */ +#include #include "patricia.h" @@ -561,6 +567,105 @@ patricia_search_exact (patricia_tree_t *patricia, prefix_t *prefix) 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 */ patricia_node_t * diff --git a/src/patricia.h b/src/patricia.h index dc67226362..3a9badd29a 100644 --- a/src/patricia.h +++ b/src/patricia.h @@ -104,6 +104,7 @@ typedef struct _patricia_tree_t { 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_best2 (patricia_tree_t *patricia, prefix_t *prefix, int inclusive); diff --git a/testing/btest/Baseline/bifs.matching_subnets/output b/testing/btest/Baseline/bifs.matching_subnets/output new file mode 100644 index 0000000000..e051d89b79 --- /dev/null +++ b/testing/btest/Baseline/bifs.matching_subnets/output @@ -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] diff --git a/testing/btest/bifs/matching_subnets.bro b/testing/btest/bifs/matching_subnets.bro new file mode 100644 index 0000000000..87effed19f --- /dev/null +++ b/testing/btest/bifs/matching_subnets.bro @@ -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; + }