node: Respect relay config on subscribe

Don't relay when a subscribe message is received, unless our relay
configuration allows us.
This commit is contained in:
cloudhead 2024-05-02 13:48:47 +02:00
parent a54180199e
commit 0d880e12e1
No known key found for this signature in database
2 changed files with 36 additions and 30 deletions

View File

@ -25,7 +25,7 @@ use radicle::node;
use radicle::node::address; use radicle::node::address;
use radicle::node::address::Store as _; use radicle::node::address::Store as _;
use radicle::node::address::{AddressBook, KnownAddress}; use radicle::node::address::{AddressBook, KnownAddress};
use radicle::node::config::{PeerConfig, Relay}; use radicle::node::config::PeerConfig;
use radicle::node::refs::Store as _; use radicle::node::refs::Store as _;
use radicle::node::routing::Store as _; use radicle::node::routing::Store as _;
use radicle::node::seed; use radicle::node::seed;
@ -88,6 +88,9 @@ pub const MAX_CONNECTION_ATTEMPTS: usize = 3;
/// How far back from the present time should we request gossip messages when connecting to a peer, /// How far back from the present time should we request gossip messages when connecting to a peer,
/// when we come online for the first time. /// when we come online for the first time.
pub const INITIAL_SUBSCRIBE_BACKLOG_DELTA: LocalDuration = LocalDuration::from_mins(60 * 24); pub const INITIAL_SUBSCRIBE_BACKLOG_DELTA: LocalDuration = LocalDuration::from_mins(60 * 24);
/// When subscribing, what margin of error do we give ourselves. A gigher delta means we ask for
/// messages further back than strictly necessary, to account for missed messages.
pub const SUBSCRIBE_BACKLOG_DELTA: LocalDuration = LocalDuration::from_mins(3);
/// Minimum amount of time to wait before reconnecting to a peer. /// Minimum amount of time to wait before reconnecting to a peer.
pub const MIN_RECONNECTION_DELTA: LocalDuration = LocalDuration::from_secs(3); pub const MIN_RECONNECTION_DELTA: LocalDuration = LocalDuration::from_secs(3);
/// Maximum amount of time to wait before reconnecting to a peer. /// Maximum amount of time to wait before reconnecting to a peer.
@ -1334,7 +1337,7 @@ where
&mut self, &mut self,
relayer_addr: &Address, relayer_addr: &Address,
announcement: &Announcement, announcement: &Announcement,
) -> Result<Relay, session::Error> { ) -> Result<bool, session::Error> {
if !announcement.verify() { if !announcement.verify() {
return Err(session::Error::Misbehavior); return Err(session::Error::Misbehavior);
} }
@ -1346,20 +1349,15 @@ where
// Ignore our own announcements, in case the relayer sent one by mistake. // Ignore our own announcements, in case the relayer sent one by mistake.
if announcer == self.nid() { if announcer == self.nid() {
return Ok(Relay::Never); return Ok(false);
} }
let now = self.clock; let now = self.clock;
let timestamp = message.timestamp(); let timestamp = message.timestamp();
// To avoid spamming peers on startup with historical gossip messages, // To avoid spamming peers on startup with historical gossip messages,
// don't relay messages that are too old. We make an exception for node announcements, // don't relay messages that are too old. We make an exception for node announcements,
// since they are cached, and will hence often carry old timestamps. // since they are cached, and will hence often carry old timestamps.
let relay = if message.is_node_announcement() let relay =
|| now - timestamp.to_local_time() <= MAX_TIME_DELTA message.is_node_announcement() || now - timestamp.to_local_time() <= MAX_TIME_DELTA;
{
self.config.relay
} else {
Relay::Never
};
// Don't allow messages from too far in the future. // Don't allow messages from too far in the future.
if timestamp.saturating_sub(now.as_millis()) > MAX_TIME_DELTA.as_millis() as u64 { if timestamp.saturating_sub(now.as_millis()) > MAX_TIME_DELTA.as_millis() as u64 {
@ -1379,12 +1377,12 @@ where
Ok(node) => { Ok(node) => {
if node.is_none() { if node.is_none() {
debug!(target: "service", "Ignoring announcement from unknown node {announcer} (t={timestamp})"); debug!(target: "service", "Ignoring announcement from unknown node {announcer} (t={timestamp})");
return Ok(Relay::Never); return Ok(false);
} }
} }
Err(e) => { Err(e) => {
error!(target: "service", "Error looking up node in address book: {e}"); error!(target: "service", "Error looking up node in address book: {e}");
return Ok(Relay::Never); return Ok(false);
} }
} }
} }
@ -1394,12 +1392,12 @@ where
Ok(fresh) => { Ok(fresh) => {
if !fresh { if !fresh {
debug!(target: "service", "Ignoring stale announcement from {announcer} (t={timestamp})"); debug!(target: "service", "Ignoring stale announcement from {announcer} (t={timestamp})");
return Ok(Relay::Never); return Ok(false);
} }
} }
Err(e) => { Err(e) => {
error!(target: "service", "Error updating gossip entry from {announcer}: {e}"); error!(target: "service", "Error updating gossip entry from {announcer}: {e}");
return Ok(Relay::Never); return Ok(false);
} }
} }
@ -1419,12 +1417,12 @@ where
Ok(synced) => { Ok(synced) => {
if synced.is_empty() { if synced.is_empty() {
trace!(target: "service", "No routes updated by inventory announcement from {announcer}"); trace!(target: "service", "No routes updated by inventory announcement from {announcer}");
return Ok(Relay::Never); return Ok(false);
} }
} }
Err(e) => { Err(e) => {
error!(target: "service", "Error processing inventory from {announcer}: {e}"); error!(target: "service", "Error processing inventory from {announcer}: {e}");
return Ok(Relay::Never); return Ok(false);
} }
} }
@ -1473,7 +1471,7 @@ where
// Empty announcements can be safely ignored. // Empty announcements can be safely ignored.
let Some(refs) = NonEmpty::from_vec(message.refs.to_vec()) else { let Some(refs) = NonEmpty::from_vec(message.refs.to_vec()) else {
debug!(target: "service", "Skipping fetch, no refs in announcement for {} (t={timestamp})", message.rid); debug!(target: "service", "Skipping fetch, no refs in announcement for {} (t={timestamp})", message.rid);
return Ok(Relay::Never); return Ok(false);
}; };
// We update inventories when receiving ref announcements, as these could come // We update inventories when receiving ref announcements, as these could come
// from a new repository being initialized. // from a new repository being initialized.
@ -1526,7 +1524,7 @@ where
"Ignoring refs announcement from {announcer}: repository {} isn't seeded (t={timestamp})", "Ignoring refs announcement from {announcer}: repository {} isn't seeded (t={timestamp})",
message.rid message.rid
); );
return Ok(Relay::Never); return Ok(false);
} }
// Refs can be relayed by peers who don't have the data in storage, // Refs can be relayed by peers who don't have the data in storage,
// therefore we only check whether we are connected to the *announcer*, // therefore we only check whether we are connected to the *announcer*,
@ -1600,7 +1598,7 @@ where
} }
} }
} }
Ok(Relay::Never) Ok(false)
} }
pub fn handle_info(&mut self, remote: NodeId, info: &Info) -> Result<(), session::Error> { pub fn handle_info(&mut self, remote: NodeId, info: &Info) -> Result<(), session::Error> {
@ -1624,6 +1622,8 @@ where
remote: &NodeId, remote: &NodeId,
message: Message, message: Message,
) -> Result<(), session::Error> { ) -> Result<(), session::Error> {
let local = self.node_id();
let relay = self.config.is_relay();
let Some(peer) = self.sessions.get_mut(remote) else { let Some(peer) = self.sessions.get_mut(remote) else {
warn!(target: "service", "Session not found for {remote}"); warn!(target: "service", "Session not found for {remote}");
return Ok(()); return Ok(());
@ -1652,15 +1652,7 @@ where
let relayer_addr = peer.addr.clone(); let relayer_addr = peer.addr.clone();
let announcer = ann.node; let announcer = ann.node;
let relay = match self.handle_announcement(&relayer_addr, &ann)? { if self.handle_announcement(&relayer_addr, &ann)? && self.config.is_relay() {
// In "auto" mode, we only relay if we're a public seed node.
// This reduces traffic for private nodes, as well as message redundancy.
Relay::Auto => !self.config.external_addresses.is_empty(),
Relay::Never => false,
Relay::Always => true,
};
if relay {
// Choose peers we should relay this message to. // Choose peers we should relay this message to.
// 1. Don't relay to the peer who sent us this message. // 1. Don't relay to the peer who sent us this message.
// 2. Don't relay to the peer who signed this announcement. // 2. Don't relay to the peer who signed this announcement.
@ -1695,7 +1687,10 @@ where
if ann.node == *remote { if ann.node == *remote {
continue; continue;
} }
self.outbox.write(peer, ann.into()); // Only send messages if we're a relay, or it's our own messages.
if relay || ann.node == local {
self.outbox.write(peer, ann.into());
}
} }
} }
Err(e) => { Err(e) => {
@ -1816,7 +1811,7 @@ where
// If this is our first connection to the network, we just ask for a fixed backlog // If this is our first connection to the network, we just ask for a fixed backlog
// of messages to get us started. // of messages to get us started.
let since = match self.db.gossip().last() { let since = match self.db.gossip().last() {
Ok(Some(last)) => Timestamp::from(last.to_local_time() - MAX_TIME_DELTA), Ok(Some(last)) => Timestamp::from(last.to_local_time() - SUBSCRIBE_BACKLOG_DELTA),
Ok(None) => (*now - INITIAL_SUBSCRIBE_BACKLOG_DELTA).into(), Ok(None) => (*now - INITIAL_SUBSCRIBE_BACKLOG_DELTA).into(),
Err(e) => { Err(e) => {
error!(target: "service", "Error getting the lastest gossip message from storage: {e}"); error!(target: "service", "Error getting the lastest gossip message from storage: {e}");

View File

@ -347,6 +347,17 @@ impl Config {
self.peer(id).is_some() self.peer(id).is_some()
} }
/// Are we a relay node? This determines what we do with gossip messages from other peers.
pub fn is_relay(&self) -> bool {
match self.relay {
// In "auto" mode, we only relay if we're a public seed node.
// This reduces traffic for private nodes, as well as message redundancy.
Relay::Auto => !self.external_addresses.is_empty(),
Relay::Never => false,
Relay::Always => true,
}
}
pub fn features(&self) -> node::Features { pub fn features(&self) -> node::Features {
node::Features::SEED node::Features::SEED
} }