Add find_next_available_key function to prevent overwriting buckets
This commit is contained in:
parent
48cdb97a9f
commit
40f1e88a31
|
@ -255,14 +255,20 @@ pub struct BridgeTable {
|
||||||
/// In some instances a single bridge may need to be added to a bucket
|
/// In some instances a single bridge may need to be added to a bucket
|
||||||
/// In that case, a spare bucket will be removed from the set of spare bridges. One
|
/// In that case, a spare bucket will be removed from the set of spare bridges. One
|
||||||
pub unallocated_bridges: Vec<BridgeLine>,
|
pub unallocated_bridges: Vec<BridgeLine>,
|
||||||
|
// To prevent issues with a counter for the hashmap keys, we keep a list of keys that
|
||||||
|
// no longer match any buckets that can be used before increasing the counter
|
||||||
|
pub recycleable_keys: Vec<u32>,
|
||||||
|
// We maintain a list of keys that have been blocked, as well as the date of their blocking
|
||||||
|
// so that they can be repurposed with new buckets eventually
|
||||||
|
pub blocked_keys: Vec<(u32, u32)>,
|
||||||
/// The date the buckets were last encrypted to make the encbucket.
|
/// The date the buckets were last encrypted to make the encbucket.
|
||||||
/// The encbucket must be rebuilt each day so that the Bucket
|
/// The encbucket must be rebuilt each day so that the Bucket
|
||||||
/// Reachability credentials in the buckets can be refreshed.
|
/// Reachability credentials in the buckets can be refreshed.
|
||||||
pub date_last_enc: u32,
|
pub date_last_enc: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invariant: the lengths of the keys and buckets vectors are the same.
|
// Invariant: the lengths of the keys and bucket hashmap are the same.
|
||||||
// The encbuckets vector only gets updated when encrypt_table is called.
|
// The encbuckets hashmap only gets updated when encrypt_table is called.
|
||||||
|
|
||||||
impl BridgeTable {
|
impl BridgeTable {
|
||||||
/// Get the number of buckets in the bridge table
|
/// Get the number of buckets in the bridge table
|
||||||
|
@ -270,6 +276,54 @@ impl BridgeTable {
|
||||||
self.buckets.len()
|
self.buckets.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get today's (real or simulated) date
|
||||||
|
fn today(&self) -> u32 {
|
||||||
|
// We will not encounter negative Julian dates (~6700 years ago)
|
||||||
|
// or ones larger than 32 bits
|
||||||
|
(time::OffsetDateTime::now_utc().date())
|
||||||
|
.to_julian_day()
|
||||||
|
.try_into()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since buckets are moved around in the bridge_table, finding a lookup key that
|
||||||
|
// does not overwrite existing bridges could become an issue. We keep a list
|
||||||
|
// of recycleable lookup keys from buckets that have been removed and prioritize
|
||||||
|
// this list before increasing the counter
|
||||||
|
fn find_next_available_key(&mut self) -> u32 {
|
||||||
|
// First check if there are any blocked indexes that are old enough to be replaced
|
||||||
|
if !self.blocked_keys.is_empty()
|
||||||
|
&& self.blocked_keys.iter().any(|&x| x.1 > self.today() + 30)
|
||||||
|
//Perhaps 30 should be changed to a later/earlier time
|
||||||
|
{
|
||||||
|
let blocked_keys_clone = self.blocked_keys.clone();
|
||||||
|
// If so, separate them from the fresh blockages
|
||||||
|
let (expired, fresh): (Vec<(u32, u32)>, Vec<(u32, u32)>) = blocked_keys_clone
|
||||||
|
.into_iter()
|
||||||
|
.partition(|&x| x.1 > self.today() + 30);
|
||||||
|
for item in expired {
|
||||||
|
let new_item = item.0;
|
||||||
|
//and add them to the recyclable keys
|
||||||
|
self.recycleable_keys.push(new_item);
|
||||||
|
}
|
||||||
|
// update the blocked_keys vector to only include the fresh keys
|
||||||
|
self.blocked_keys = fresh
|
||||||
|
}
|
||||||
|
if self.recycleable_keys.is_empty() {
|
||||||
|
let mut test_index = 1;
|
||||||
|
let mut test_counter = self.counter.wrapping_add(test_index);
|
||||||
|
while self.buckets.contains_key(&test_counter) {
|
||||||
|
for i in 0..5000 {
|
||||||
|
test_index += i;
|
||||||
|
test_counter = self.counter.wrapping_add(test_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.counter = self.counter.wrapping_add(test_index);
|
||||||
|
self.counter
|
||||||
|
} else {
|
||||||
|
self.recycleable_keys.pop().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Append a new bucket to the bridge table, returning its index
|
/// Append a new bucket to the bridge table, returning its index
|
||||||
pub fn new_bucket(&mut self, bucket: &[BridgeLine; MAX_BRIDGES_PER_BUCKET]) -> u32 {
|
pub fn new_bucket(&mut self, bucket: &[BridgeLine; MAX_BRIDGES_PER_BUCKET]) -> u32 {
|
||||||
// Pick a random key to encrypt this bucket
|
// Pick a random key to encrypt this bucket
|
||||||
|
@ -277,17 +331,18 @@ impl BridgeTable {
|
||||||
let mut key: [u8; 16] = [0; 16];
|
let mut key: [u8; 16] = [0; 16];
|
||||||
rng.fill_bytes(&mut key);
|
rng.fill_bytes(&mut key);
|
||||||
// Increase the counter to identify the bucket, wrap value if beyond u32::MAX
|
// Increase the counter to identify the bucket, wrap value if beyond u32::MAX
|
||||||
self.counter = self.counter.wrapping_add(1);
|
let index = self.find_next_available_key();
|
||||||
self.keys.insert(self.counter, key);
|
//self.counter = self.counter.wrapping_add(1);
|
||||||
self.buckets.insert(self.counter, *bucket);
|
self.keys.insert(index, key);
|
||||||
|
self.buckets.insert(index, *bucket);
|
||||||
// TODO: maybe we don't need this if the hashtable can keep track of available bridges
|
// TODO: maybe we don't need this if the hashtable can keep track of available bridges
|
||||||
// Mark the new bridges as available
|
// Mark the new bridges as available
|
||||||
for (i, b) in bucket.iter().enumerate() {
|
for (i, b) in bucket.iter().enumerate() {
|
||||||
if b.port > 0 {
|
if b.port > 0 {
|
||||||
if let Some(v) = self.reachable.get_mut(b) {
|
if let Some(v) = self.reachable.get_mut(b) {
|
||||||
v.push((self.counter, i));
|
v.push((index, i));
|
||||||
} else {
|
} else {
|
||||||
let v = vec![(self.counter, i)];
|
let v = vec![(index, i)];
|
||||||
self.reachable.insert(*b, v);
|
self.reachable.insert(*b, v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -445,6 +445,7 @@ impl BridgeAuth {
|
||||||
// Get the first spare and remove it from the spares set.
|
// Get the first spare and remove it from the spares set.
|
||||||
let spare = *self.bridge_table.spares.iter().next().unwrap();
|
let spare = *self.bridge_table.spares.iter().next().unwrap();
|
||||||
self.bridge_table.spares.remove(&spare);
|
self.bridge_table.spares.remove(&spare);
|
||||||
|
self.bridge_table.recycleable_keys.push(spare);
|
||||||
// Get the actual bridges from the spare bucket
|
// Get the actual bridges from the spare bucket
|
||||||
let spare_bucket = match self.bridge_table.buckets.remove(&spare) {
|
let spare_bucket = match self.bridge_table.buckets.remove(&spare) {
|
||||||
Some(spare_bucket) => spare_bucket,
|
Some(spare_bucket) => spare_bucket,
|
||||||
|
@ -567,6 +568,9 @@ impl BridgeAuth {
|
||||||
// set.
|
// set.
|
||||||
let spare = *self.bridge_table.spares.iter().next().unwrap();
|
let spare = *self.bridge_table.spares.iter().next().unwrap();
|
||||||
self.bridge_table.spares.remove(&spare);
|
self.bridge_table.spares.remove(&spare);
|
||||||
|
self.bridge_table
|
||||||
|
.blocked_keys
|
||||||
|
.push((*bucketnum, self.today()));
|
||||||
// Add a blockage migration from this bucket to the spare
|
// Add a blockage migration from this bucket to the spare
|
||||||
self.blockage_migration_table
|
self.blockage_migration_table
|
||||||
.table
|
.table
|
||||||
|
|
Loading…
Reference in New Issue