Commit 95afdc44 authored by rtenneti@chromium.org's avatar rtenneti@chromium.org

Narrow the strike register window in both directions when tracking many

recent nonces.  This reduces the amount of time that nonces with future
timestamps are tracked, which allows accepting more nonces from clients
with clocks that agree with the strike register's clock.

Not flag protected.

Merge internal change: 69497947

R=wtc@chromium.org

Review URL: https://codereview.chromium.org/389393005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@283445 0039d316-1c4b-4281-b951-d872f2087c98
parent b3e99303
......@@ -4,8 +4,12 @@
#include "net/quic/crypto/strike_register.h"
#include <limits>
#include "base/logging.h"
using std::make_pair;
using std::max;
using std::min;
using std::pair;
using std::set;
......@@ -77,7 +81,7 @@ class StrikeRegister::InternalNode {
// kCreationTimeFromInternalEpoch contains the number of seconds between the
// start of the internal epoch and the creation time. This allows us
// to consider times that are before the creation time.
static const uint32 kCreationTimeFromInternalEpoch = 63115200.0; // 2 years.
static const uint32 kCreationTimeFromInternalEpoch = 63115200; // 2 years.
void StrikeRegister::ValidateStrikeRegisterConfig(unsigned max_entries) {
// We only have 23 bits of index available.
......@@ -127,7 +131,7 @@ void StrikeRegister::Reset() {
}
bool StrikeRegister::Insert(const uint8 nonce[32],
const uint32 current_time_external) {
uint32 current_time_external) {
// Make space for the insertion if the strike register is full.
while (external_node_free_head_ == kNil ||
internal_node_free_head_ == kNil) {
......@@ -140,17 +144,13 @@ bool StrikeRegister::Insert(const uint8 nonce[32],
if (memcmp(nonce + sizeof(current_time), orbit_, sizeof(orbit_))) {
return false;
}
const uint32 nonce_time = ExternalTimeToInternal(TimeFromBytes(nonce));
// We have dropped one or more nonces with a time value of |horizon_ - 1|, so
// we have to reject anything with a timestamp less than or equal to that.
if (nonce_time < horizon_) {
return false;
}
// Check that the timestamp is in the current window.
if ((current_time > window_secs_ &&
nonce_time < (current_time - window_secs_)) ||
nonce_time > (current_time + window_secs_)) {
// Check that the timestamp is in the valid range.
pair<uint32, uint32> valid_range =
StrikeRegister::GetValidRange(current_time);
if (nonce_time < valid_range.first || nonce_time > valid_range.second) {
return false;
}
......@@ -270,15 +270,16 @@ const uint8* StrikeRegister::orbit() const {
return orbit_;
}
uint32 StrikeRegister::EffectiveWindowSecs(
const uint32 current_time_external) const {
const uint32 future_horizon =
ExternalTimeToInternal(current_time_external) + window_secs_;
if (horizon_ >= future_horizon) {
uint32 StrikeRegister::GetCurrentValidWindowSecs(
uint32 current_time_external) const {
uint32 current_time = ExternalTimeToInternal(current_time_external);
pair<uint32, uint32> valid_range = StrikeRegister::GetValidRange(
current_time);
if (valid_range.second >= valid_range.first) {
return valid_range.second - current_time + 1;
} else {
return 0;
}
return min(future_horizon - horizon_, 2 * window_secs_);
}
void StrikeRegister::Validate() {
......@@ -318,6 +319,33 @@ uint32 StrikeRegister::TimeFromBytes(const uint8 d[4]) {
static_cast<uint32>(d[3]);
}
pair<uint32, uint32> StrikeRegister::GetValidRange(
uint32 current_time_internal) const {
if (current_time_internal < horizon_) {
// Empty valid range.
return make_pair(std::numeric_limits<uint32>::max(), 0);
}
uint32 lower_bound;
if (current_time_internal >= window_secs_) {
lower_bound = max(horizon_, current_time_internal - window_secs_);
} else {
lower_bound = horizon_;
}
// Also limit the upper range based on horizon_. This makes the
// strike register reject inserts that are far in the future and
// would consume strike register resources for a long time. This
// allows the strike server to degrade optimally in cases where the
// insert rate exceeds |max_entries_ / (2 * window_secs_)| entries
// per second.
uint32 upper_bound =
current_time_internal + min(current_time_internal - horizon_,
window_secs_);
return make_pair(lower_bound, upper_bound);
}
uint32 StrikeRegister::ExternalTimeToInternal(uint32 external_time) const {
return external_time - internal_epoch_;
}
......
......@@ -116,14 +116,14 @@ class NET_EXPORT_PRIVATE StrikeRegister {
// 20 bytes of random data
//
// Otherwise, it inserts |nonce| into the observed set and returns true.
bool Insert(const uint8 nonce[32], const uint32 current_time);
bool Insert(const uint8 nonce[32], uint32 current_time);
// orbit returns a pointer to the 8-byte orbit value for this
// strike-register.
const uint8* orbit() const;
// Time window for which the strike register has complete information.
uint32 EffectiveWindowSecs(const uint32 current_time_external) const;
uint32 GetCurrentValidWindowSecs(uint32 current_time_external) const;
// This is a debugging aid which checks the tree for sanity.
void Validate();
......@@ -134,6 +134,12 @@ class NET_EXPORT_PRIVATE StrikeRegister {
// TimeFromBytes returns a big-endian uint32 from |d|.
static uint32 TimeFromBytes(const uint8 d[4]);
// Range of internal times for which the strike register has
// complete information. A nonce is within the valid range of the
// strike register if:
// valid_range.first <= nonce_time_internal <= valid_range.second
std::pair<uint32, uint32> GetValidRange(uint32 current_time_internal) const;
// ExternalTimeToInternal converts an external time value into an internal
// time value using |internal_epoch_|.
uint32 ExternalTimeToInternal(uint32 external_time) const;
......@@ -174,6 +180,7 @@ class NET_EXPORT_PRIVATE StrikeRegister {
// time.
const uint32 internal_epoch_;
uint8 orbit_[8];
// The strike register will reject nonces with internal times < |horizon_| .
uint32 horizon_;
uint32 internal_node_free_head_;
......
......@@ -44,11 +44,12 @@ TEST(StrikeRegisterTest, SimpleHorizon) {
SetNonce(nonce, 1000, kOrbit);
ASSERT_FALSE(set.Insert(nonce, 1000));
EXPECT_EQ(0u, set.EffectiveWindowSecs(1000 /* current time */));
EXPECT_EQ(49u, set.EffectiveWindowSecs(1050 /* current time */));
EXPECT_EQ(99u, set.EffectiveWindowSecs(1100 /* current time */));
EXPECT_EQ(199u, set.EffectiveWindowSecs(1200 /* current time */));
EXPECT_EQ(200u, set.EffectiveWindowSecs(1300 /* current time */));
EXPECT_EQ(0u, set.GetCurrentValidWindowSecs(1000 /* current time */));
EXPECT_EQ(0u, set.GetCurrentValidWindowSecs(1100 /* current time */));
EXPECT_EQ(1u, set.GetCurrentValidWindowSecs(1101 /* current time */));
EXPECT_EQ(50u, set.GetCurrentValidWindowSecs(1150 /* current time */));
EXPECT_EQ(100u, set.GetCurrentValidWindowSecs(1200 /* current time */));
EXPECT_EQ(101u, set.GetCurrentValidWindowSecs(1300 /* current time */));
}
TEST(StrikeRegisterTest, NoStartupMode) {
......@@ -62,11 +63,11 @@ TEST(StrikeRegisterTest, NoStartupMode) {
ASSERT_TRUE(set.Insert(nonce, 1000));
ASSERT_FALSE(set.Insert(nonce, 1000));
EXPECT_EQ(200u, set.EffectiveWindowSecs(1000 /* current time */));
EXPECT_EQ(200u, set.EffectiveWindowSecs(1050 /* current time */));
EXPECT_EQ(200u, set.EffectiveWindowSecs(1100 /* current time */));
EXPECT_EQ(200u, set.EffectiveWindowSecs(1200 /* current time */));
EXPECT_EQ(200u, set.EffectiveWindowSecs(1300 /* current time */));
EXPECT_EQ(101u, set.GetCurrentValidWindowSecs(1000 /* current time */));
EXPECT_EQ(101u, set.GetCurrentValidWindowSecs(1050 /* current time */));
EXPECT_EQ(101u, set.GetCurrentValidWindowSecs(1100 /* current time */));
EXPECT_EQ(101u, set.GetCurrentValidWindowSecs(1200 /* current time */));
EXPECT_EQ(101u, set.GetCurrentValidWindowSecs(1300 /* current time */));
}
TEST(StrikeRegisterTest, WindowFuture) {
......@@ -98,7 +99,7 @@ TEST(StrikeRegisterTest, OneValue) {
StrikeRegister::DENY_REQUESTS_AT_STARTUP);
uint8 nonce[32];
SetNonce(nonce, 1101, kOrbit);
ASSERT_TRUE(set.Insert(nonce, 1100));
ASSERT_TRUE(set.Insert(nonce, 1101));
}
TEST(StrikeRegisterTest, RejectDuplicate) {
......@@ -108,8 +109,8 @@ TEST(StrikeRegisterTest, RejectDuplicate) {
StrikeRegister::DENY_REQUESTS_AT_STARTUP);
uint8 nonce[32];
SetNonce(nonce, 1101, kOrbit);
ASSERT_TRUE(set.Insert(nonce, 1100));
ASSERT_FALSE(set.Insert(nonce, 1100));
ASSERT_TRUE(set.Insert(nonce, 1101));
ASSERT_FALSE(set.Insert(nonce, 1101));
}
TEST(StrikeRegisterTest, HorizonUpdating) {
......@@ -129,25 +130,40 @@ TEST(StrikeRegisterTest, HorizonUpdating) {
ASSERT_TRUE(set.Insert(nonce[i], 1100));
}
// Effective horizon is still 2 * window.
EXPECT_EQ(200u, set.EffectiveWindowSecs(1100));
// Valid window is still equal to |window_secs + 1|.
EXPECT_EQ(101u, set.GetCurrentValidWindowSecs(1100));
// This should push the oldest value out and force the horizon to
// be updated.
SetNonce(nonce[5], 1110, kOrbit);
ASSERT_TRUE(set.Insert(nonce[5], 1100));
ASSERT_TRUE(set.Insert(nonce[5], 1110));
// Effective horizon is computed based on the timestamp of the
// value that was pushed out.
EXPECT_EQ(98u, set.EffectiveWindowSecs(1100));
EXPECT_EQ(9u, set.GetCurrentValidWindowSecs(1110));
SetNonce(nonce[5], 1111, kOrbit);
EXPECT_TRUE(set.Insert(nonce[5], 1100));
EXPECT_EQ(97u, set.EffectiveWindowSecs(1100));
EXPECT_TRUE(set.Insert(nonce[5], 1110));
EXPECT_EQ(8u, set.GetCurrentValidWindowSecs(1110));
// This should be behind the horizon now:
SetNonce(nonce[5], 1101, kOrbit);
nonce[5][31] = 10;
ASSERT_FALSE(set.Insert(nonce[5], 1100));
EXPECT_FALSE(set.Insert(nonce[5], 1110));
// Insert beyond the valid range.
SetNonce(nonce[5], 1117, kOrbit);
nonce[5][31] = 2;
EXPECT_FALSE(set.Insert(nonce[5], 1110));
// Insert at the upper valid range.
SetNonce(nonce[5], 1116, kOrbit);
nonce[5][31] = 1;
EXPECT_TRUE(set.Insert(nonce[5], 1110));
// This should be beyond the upper valid range now:
SetNonce(nonce[5], 1116, kOrbit);
nonce[5][31] = 2;
EXPECT_FALSE(set.Insert(nonce[5], 1110));
}
}
......@@ -228,13 +244,12 @@ class SlowStrikeRegister {
return true;
}
uint32 EffectiveWindowSecs(const uint32 current_time_external) const {
const uint32 future_horizon =
ExternalTimeToInternal(current_time_external) + window_secs_;
if (horizon_ > future_horizon) {
uint32 GetCurrentValidWindowSecs(const uint32 current_time_external) const {
const uint32 current_time = ExternalTimeToInternal(current_time_external);
if (horizon_ > current_time) {
return 0;
}
return min(future_horizon - horizon_, 2 * window_secs_);
return 1 + min(current_time - horizon_, window_secs_);
}
private:
......@@ -304,8 +319,8 @@ TEST_F(StrikeRegisterStressTest, InOrderInsertion) {
EXPECT_EQ(r1, r2);
// Inserts succeed after the startup period.
EXPECT_EQ(time > current_time + window, r1);
EXPECT_EQ(s1->EffectiveWindowSecs(time),
s2->EffectiveWindowSecs(time));
EXPECT_EQ(s1->GetCurrentValidWindowSecs(time),
s2->GetCurrentValidWindowSecs(time));
if (i % 10 == 0) {
s1->Validate();
......@@ -367,8 +382,8 @@ TEST_F(StrikeRegisterStressTest, Stress) {
const bool r2 = s2->Insert(nonce, time, time);
const bool r1 = s1->Insert(nonce, time);
EXPECT_EQ(r1, r2);
EXPECT_EQ(s1->EffectiveWindowSecs(time),
s2->EffectiveWindowSecs(time));
EXPECT_EQ(s1->GetCurrentValidWindowSecs(time),
s2->GetCurrentValidWindowSecs(time));
if (i % 10 == 0) {
s1->Validate();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment