From: bert hubert Date: Wed, 9 Sep 2015 09:17:45 +0000 (+0200) Subject: make our question hash for query distribution case insensitive. add tests that check... X-Git-Tag: dnsdist-1.0.0-alpha1~248^2~55 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6200227b6b3cfd8796d6cc75e8cf29bcec586f0c;p=thirdparty%2Fpdns.git make our question hash for query distribution case insensitive. add tests that check this is the case. add rudimentary test that checks if our hash is reasonably flat. --- diff --git a/pdns/dns.cc b/pdns/dns.cc index d790a7da48..2f66c5b10b 100644 --- a/pdns/dns.cc +++ b/pdns/dns.cc @@ -145,7 +145,7 @@ uint32_t hashQuestion(const char* packet, uint16_t len, uint32_t init) while((labellen=*pos++) && pos < end) { if(pos + labellen + 1 > end) // include length field in hash return 0; - ret=burtle(pos, labellen+1, ret); + ret=burtleCI(pos, labellen+1, ret); pos += labellen; } return ret; diff --git a/pdns/misc.cc b/pdns/misc.cc index 1379315dcf..bea87be2b1 100644 --- a/pdns/misc.cc +++ b/pdns/misc.cc @@ -922,6 +922,47 @@ uint32_t burtle(const unsigned char* k, uint32_t length, uint32_t initval) return c; } +uint32_t burtleCI(const unsigned char* k, uint32_t length, uint32_t initval) +{ + uint32_t a,b,c,len; + + /* Set up the internal state */ + len = length; + a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ + c = initval; /* the previous hash value */ + + /*---------------------------------------- handle most of the key */ + while (len >= 12) { + a += (tolower(k[0]) +((uint32_t)tolower(k[1])<<8) +((uint32_t)tolower(k[2])<<16) +((uint32_t)tolower(k[3])<<24)); + b += (tolower(k[4]) +((uint32_t)tolower(k[5])<<8) +((uint32_t)tolower(k[6])<<16) +((uint32_t)tolower(k[7])<<24)); + c += (tolower(k[8]) +((uint32_t)tolower(k[9])<<8) +((uint32_t)tolower(k[10])<<16)+((uint32_t)tolower(k[11])<<24)); + burtlemix(a,b,c); + k += 12; len -= 12; + } + + /*------------------------------------- handle the last 11 bytes */ + c += length; + switch(len) { /* all the case statements fall through */ + case 11: c+=((uint32_t)tolower(k[10])<<24); + case 10: c+=((uint32_t)tolower(k[9])<<16); + case 9 : c+=((uint32_t)tolower(k[8])<<8); + /* the first byte of c is reserved for the length */ + case 8 : b+=((uint32_t)tolower(k[7])<<24); + case 7 : b+=((uint32_t)tolower(k[6])<<16); + case 6 : b+=((uint32_t)tolower(k[5])<<8); + case 5 : b+=tolower(k[4]); + case 4 : a+=((uint32_t)tolower(k[3])<<24); + case 3 : a+=((uint32_t)tolower(k[2])<<16); + case 2 : a+=((uint32_t)tolower(k[1])<<8); + case 1 : a+=tolower(k[0]); + /* case 0: nothing left to add */ + } + burtlemix(a,b,c); + /*-------------------------------------------- report the result */ + return c; +} + + bool setSocketTimestamps(int fd) { #ifdef SO_TIMESTAMP diff --git a/pdns/misc.hh b/pdns/misc.hh index 63816c8a88..0c6c8cd8ef 100644 --- a/pdns/misc.hh +++ b/pdns/misc.hh @@ -616,6 +616,7 @@ unsigned int getFilenumLimit(bool hardOrSoft=0); void setFilenumLimit(unsigned int lim); bool readFileIfThere(const char* fname, std::string* line); uint32_t burtle(const unsigned char* k, uint32_t lengh, uint32_t init); +uint32_t burtleCI(const unsigned char* k, uint32_t lengh, uint32_t init); bool setSocketTimestamps(int fd); //! Sets the socket into blocking mode. diff --git a/pdns/test-dnsname_cc.cc b/pdns/test-dnsname_cc.cc index 4c7f1e9db9..54dd369026 100644 --- a/pdns/test-dnsname_cc.cc +++ b/pdns/test-dnsname_cc.cc @@ -209,6 +209,37 @@ BOOST_AUTO_TEST_CASE(test_Append) { BOOST_CHECK(dn == DNSName("www.powerdns.com.")); } +BOOST_AUTO_TEST_CASE(test_QuestionHash) { + vector packet; + reportBasicTypes(); + DNSPacketWriter dpw1(packet, "www.ds9a.nl.", QType::AAAA); + + auto hash1=hashQuestion((char*)&packet[0], packet.size(), 0); + DNSPacketWriter dpw2(packet, "wWw.Ds9A.nL.", QType::AAAA); + auto hash2=hashQuestion((char*)&packet[0], packet.size(), 0); + BOOST_CHECK_EQUAL(hash1, hash2); + + vector counts(1500); + + for(unsigned int n=0; n < 100000; ++n) { + packet.clear(); + DNSPacketWriter dpw1(packet, std::to_string(n)+"."+std::to_string(n*2)+".", QType::AAAA); + counts[hashQuestion((char*)&packet[0], packet.size(), 0) % counts.size()]++; + } + + double sum = std::accumulate(std::begin(counts), std::end(counts), 0.0); + double m = sum / counts.size(); + + double accum = 0.0; + std::for_each (std::begin(counts), std::end(counts), [&](const double d) { + accum += (d - m) * (d - m); + }); + + double stdev = sqrt(accum / (counts.size()-1)); + BOOST_CHECK(stdev < 10); +} + + BOOST_AUTO_TEST_CASE(test_packetParse) { vector packet; reportBasicTypes();