diff --git a/src/bro.bif b/src/bro.bif index d3bbd7c072..0aa6850b18 100644 --- a/src/bro.bif +++ b/src/bro.bif @@ -3414,10 +3414,16 @@ function bro_has_ipv6%(%) : bool function unique_id%(prefix: string%) : string %{ char tmp[20]; - uint64 uid = calculate_unique_id(); + uint64 uid = calculate_unique_id(BRO_SCRIPT_UID_POOL); return new StringVal(uitoa_n(uid, tmp, sizeof(tmp), 62, prefix->CheckString())); %} +function unique_id_from%(pool: string, prefix: string%) : string + %{ + char tmp[20]; + uint64 uid = calculate_unique_id(string((const char *)pool->Bytes(), pool->Len())); + return new StringVal(uitoa_n(uid, tmp, sizeof(tmp), 62, prefix->CheckString())); + %} %%{ #include #include diff --git a/src/util.cc b/src/util.cc index 528a505f60..70c65713af 100644 --- a/src/util.cc +++ b/src/util.cc @@ -1206,15 +1206,30 @@ int time_compare(struct timeval* tv_a, struct timeval* tv_b) return tv_a->tv_sec - tv_b->tv_sec; } -static uint64 uid_counter; // Counter for unique IDs. -static uint64 uid_instance; // Instance ID, computed once. +struct BroUidEntry +{ + uint64 instance; + uint64 counter; + BroUidEntry(const uint64 i) + : instance(i), counter(0) { } +}; + +static std::map uid_pool; +static const std::string default_pool = string(BRO_DEFAULT_UID_POOL); uint64 calculate_unique_id() { - if ( uid_instance == 0 ) - { - // This is the first time we need a UID. + return calculate_unique_id(default_pool); + } +uint64 calculate_unique_id(const std::string& pool) + { + uint64 uid_instance = 0; + std::map::iterator pool_iter = uid_pool.find(pool); + if ( pool_iter == uid_pool.end() ) + { + // This is the first time we need a UID for this pool. + const size_t pool_sz = (pool.length() < 32) ? pool.length() : 32; //Only keep the first 32 characters of the pool name if ( ! have_random_seed() ) { // If we don't need deterministic output (as @@ -1222,14 +1237,16 @@ uint64 calculate_unique_id() // instance ID by hashing something likely to be // globally unique. struct { - char hostname[128]; + char hostname[96]; + char pool[32]; struct timeval time; pid_t pid; int rnd; } unique; memset(&unique, 0, sizeof(unique)); // Make valgrind happy. - gethostname(unique.hostname, 128); + gethostname(unique.hostname, 96); + memcpy(unique.pool, pool.c_str(), pool_sz); unique.hostname[sizeof(unique.hostname)-1] = '\0'; gettimeofday(&unique.time, 0); unique.pid = getpid(); @@ -1238,22 +1255,38 @@ uint64 calculate_unique_id() uid_instance = HashKey::HashBytes(&unique, sizeof(unique)); ++uid_instance; // Now it's larger than zero. } - else - // Generate determistic UIDs. - uid_instance = 1; + { + // Generate determistic UIDs for each individual pool + uid_instance = HashKey::HashBytes(pool.c_str(), strnlen(pool.c_str(), pool_sz)); + } + // Guarantee no collisions (keep hashing until we get a unique instance) + bool found_collision = true; + while(found_collision) + { + found_collision = false; + for(pool_iter = uid_pool.begin(); pool_iter != uid_pool.end(); ++pool_iter) + { + if(pool_iter->second.instance == uid_instance) + { + found_collision = true; + uid_instance = HashKey::HashBytes(&uid_instance, sizeof(uid_instance)); + } + } + } + // Our instance is unique. Huzzah. + uid_pool.insert(std::make_pair(pool, BroUidEntry(uid_instance))); + pool_iter = uid_pool.end(); } + + if(pool_iter == uid_pool.end()) + { + pool_iter = uid_pool.find(pool); + } + assert(pool_iter != uid_pool.end()); // After all that work, wouldn't it be a shame...? + ++(pool_iter->second.counter); - // Now calculate the unique ID. - struct { - uint64 counter; - hash_t instance; - } key; - - key.counter = ++uid_counter; - key.instance = uid_instance; - - uint64_t h = HashKey::HashBytes(&key, sizeof(key)); + uint64_t h = HashKey::HashBytes(&(pool_iter->second), sizeof(pool_iter->second)); return h; } diff --git a/src/util.h b/src/util.h index 82c86da950..e104169af2 100644 --- a/src/util.h +++ b/src/util.h @@ -226,7 +226,10 @@ extern int time_compare(struct timeval* tv_a, struct timeval* tv_b); // Returns an integer that's very likely to be unique, even across Bro // instances. +#define BRO_DEFAULT_UID_POOL "bro" +#define BRO_SCRIPT_UID_POOL "bro script" extern uint64 calculate_unique_id(); +extern uint64 calculate_unique_id(const std::string& pool); // For now, don't use hash_maps - they're not fully portable. #if 0 diff --git a/testing/btest/bifs/unique_id-rnd.bro b/testing/btest/bifs/unique_id-rnd.bro index d3b3b85849..fdb04e05bc 100644 --- a/testing/btest/bifs/unique_id-rnd.bro +++ b/testing/btest/bifs/unique_id-rnd.bro @@ -7,3 +7,6 @@ print unique_id("A-"); print unique_id("B-"); print unique_id("C-"); +print unique_id_from("alpha", "D-"); +print unique_id_from("beta", "E-"); +print unique_id_from("beta", "F-"); diff --git a/testing/btest/bifs/unique_id.bro b/testing/btest/bifs/unique_id.bro index d421803aa0..1451426556 100644 --- a/testing/btest/bifs/unique_id.bro +++ b/testing/btest/bifs/unique_id.bro @@ -5,3 +5,6 @@ print unique_id("A-"); print unique_id("B-"); print unique_id("C-"); +print unique_id_from("alpha", "D-"); +print unique_id_from("beta", "E-"); +print unique_id_from("beta", "F-"); diff --git a/testing/btest/btest.cfg b/testing/btest/btest.cfg index 1aa7b28f25..d33d533b28 100644 --- a/testing/btest/btest.cfg +++ b/testing/btest/btest.cfg @@ -1,4 +1,5 @@ [btest] +ProfileDir = profiles TestDirs = doc bifs language core policy istate TmpDir = %(testbase)s/.tmp BaselineDir = %(testbase)s/Baseline diff --git a/testing/btest/profiles/default/finish b/testing/btest/profiles/default/finish new file mode 100755 index 0000000000..20d602bdd3 --- /dev/null +++ b/testing/btest/profiles/default/finish @@ -0,0 +1,2 @@ +#!/usr/bin/env bash + diff --git a/testing/btest/profiles/default/setup b/testing/btest/profiles/default/setup new file mode 100755 index 0000000000..20d602bdd3 --- /dev/null +++ b/testing/btest/profiles/default/setup @@ -0,0 +1,2 @@ +#!/usr/bin/env bash + diff --git a/testing/btest/profiles/default/supported b/testing/btest/profiles/default/supported new file mode 100755 index 0000000000..20d602bdd3 --- /dev/null +++ b/testing/btest/profiles/default/supported @@ -0,0 +1,2 @@ +#!/usr/bin/env bash + diff --git a/testing/btest/profiles/default/transform b/testing/btest/profiles/default/transform new file mode 100755 index 0000000000..20d602bdd3 --- /dev/null +++ b/testing/btest/profiles/default/transform @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +