Skip to content
Snippets Groups Projects
Commit 16f1bda3 authored by Nick Mathewson's avatar Nick Mathewson :game_die:
Browse files

Canonicalize and intern family representations to save memory.

This should save 1-3 MB of ram on each running arti client.

Closes #384. See also tor#27359 and proposal 298.
parent 927594c3
No related branches found
No related tags found
No related merge requests found
......@@ -63,7 +63,7 @@ pub struct Microdesc {
/// Public key used for the ntor circuit extension protocol.
ntor_onion_key: curve25519::PublicKey,
/// Declared family for this relay.
family: RelayFamily,
family: Arc<RelayFamily>,
/// List of IPv4 ports to which this relay will exit
ipv4_policy: Arc<PortPolicy>,
/// List of IPv6 ports to which this relay will exit
......@@ -111,7 +111,7 @@ impl Microdesc {
}
/// Return the relay family for this microdesc
pub fn family(&self) -> &RelayFamily {
&self.family
self.family.as_ref()
}
/// Return the ed25519 identity for this microdesc, if its
/// Ed25519 identity is well-formed.
......@@ -277,10 +277,14 @@ impl Microdesc {
.into();
// family
//
// (We don't need to add the relay's own ID to this family, as we do in
// RouterDescs: the authorities already took care of that for us.)
let family = body
.maybe(FAMILY)
.parse_args_as_str::<RelayFamily>()?
.unwrap_or_else(RelayFamily::new);
.unwrap_or_else(RelayFamily::new)
.intern();
// exit policies.
let ipv4_policy = body
......
......@@ -136,7 +136,7 @@ impl MicrodescBuilder {
Ok(Microdesc {
sha256,
ntor_onion_key,
family: self.family.clone(),
family: self.family.clone().intern(),
ipv4_policy: self.ipv4_policy.clone().intern(),
ipv6_policy: self.ipv6_policy.clone().intern(),
ed25519_id,
......
......@@ -131,9 +131,7 @@ pub struct RouterDesc {
is_extrainfo_cache: bool,
/// Declared family members for this relay. If two relays are in the
/// same family, they shouldn't be used in the same circuit.
// TODO: these families can get bulky. Perhaps we should de-duplicate
// them in a cache, like Tor does.
family: Option<RelayFamily>,
family: Arc<RelayFamily>,
/// Software and version that this relay says it's running.
platform: Option<RelayPlatform>,
/// A complete address-level policy for which IPv4 addresses this relay
......@@ -568,7 +566,21 @@ impl RouterDesc {
}
// Family
let family = body.maybe(FAMILY).parse_args_as_str::<RelayFamily>()?;
let family = {
let mut family = body
.maybe(FAMILY)
.parse_args_as_str::<RelayFamily>()?
.unwrap_or_else(RelayFamily::new);
if !family.is_empty() {
// If this family is nonempty, we add our own RSA id to it, on
// the theory that doing so will improve the odds of having a
// canonical family shared by all of the members of this family.
// If the family is empty, there's no point in adding our own ID
// to it, and doing so would only waste memory.
family.push(rsa_identity.to_rsa_identity());
}
family.intern()
};
// or-address
// Extract at most one ipv6 address from the list. It's not great,
......
......@@ -3,7 +3,10 @@
//! Families are opt-in lists of relays with the same operators,
//! used to avoid building insecure circuits.
use std::sync::Arc;
use crate::types::misc::LongIdent;
use crate::util::intern::InternCache;
use crate::{Error, Result};
use tor_llcrypto::pk::rsa::RsaIdentity;
......@@ -19,15 +22,39 @@ use tor_llcrypto::pk::rsa::RsaIdentity;
/// entries, including entries that are only nicknames.
///
/// TODO: This type probably belongs in a different crate.
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, Hash, Eq, PartialEq)]
pub struct RelayFamily(Vec<RsaIdentity>);
/// Cache of RelayFamily objects, for saving memory.
//
/// This only holds weak references to the policy objects, so we don't
/// need to worry about running out of space because of stale entries.
static FAMILY_CACHE: InternCache<RelayFamily> = InternCache::new();
impl RelayFamily {
/// Return a new empty RelayFamily.
pub fn new() -> Self {
RelayFamily::default()
}
/// Add `rsa_id` to this family.
pub fn push(&mut self, rsa_id: RsaIdentity) {
self.0.push(rsa_id);
}
/// Convert this family to a standard format (with all IDs sorted and de-duplicated).
fn normalize(&mut self) {
self.0.sort();
self.0.dedup();
}
/// Consume this family, and return a new canonical interned representation
/// of the family.
pub fn intern(mut self) -> Arc<Self> {
self.normalize();
FAMILY_CACHE.intern(self)
}
/// Does this family include the given relay?
pub fn contains(&self, rsa_id: &RsaIdentity) -> bool {
self.0.contains(rsa_id)
......@@ -38,6 +65,11 @@ impl RelayFamily {
pub fn members(&self) -> impl Iterator<Item = &RsaIdentity> {
self.0.iter()
}
/// Return true if this family has no members.
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl std::str::FromStr for RelayFamily {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment