From 3403a66d0fc9c9cfab97eddaf5adeab40dc3bf23 Mon Sep 17 00:00:00 2001 From: cloudhead Date: Wed, 22 May 2024 13:49:18 +0200 Subject: [PATCH] node: Ignore messages from newer protocol versions This allows for protocol upgrades without breaking old nodes. --- radicle-node/src/wire.rs | 2 ++ radicle-node/src/wire/frame.rs | 26 +++++++++++++++++++------- radicle-node/src/wire/protocol.rs | 12 +++++++++++- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/radicle-node/src/wire.rs b/radicle-node/src/wire.rs index d7e19588..f8d490b8 100644 --- a/radicle-node/src/wire.rs +++ b/radicle-node/src/wire.rs @@ -62,6 +62,8 @@ pub enum Error { InvalidOnionAddr(#[from] tor::OnionAddrDecodeError), #[error("invalid timestamp: {0}")] InvalidTimestamp(u64), + #[error("unknown protocol version `{0}`")] + UnknownProtocolVersion(u8), #[error("unknown address type `{0}`")] UnknownAddressType(u8), #[error("unknown message type `{0}`")] diff --git a/radicle-node/src/wire/frame.rs b/radicle-node/src/wire/frame.rs index a91de2d8..1f73f9e5 100644 --- a/radicle-node/src/wire/frame.rs +++ b/radicle-node/src/wire/frame.rs @@ -4,9 +4,11 @@ use std::{fmt, io}; use crate::{wire, wire::varint, wire::varint::VarInt, wire::Message, Link}; +/// Protocol version. +pub const PROTOCOL_VERSION: u8 = 1; /// Protocol version strings all start with the magic sequence `rad`, followed /// by a version number. -pub const PROTOCOL_VERSION: Version = Version([b'r', b'a', b'd', 0x1]); +pub const PROTOCOL_VERSION_STRING: Version = Version([b'r', b'a', b'd', PROTOCOL_VERSION]); /// Control open byte. const CONTROL_OPEN: u8 = 0; @@ -19,11 +21,18 @@ const CONTROL_EOF: u8 = 2; #[derive(Debug, PartialEq, Eq)] pub struct Version([u8; 4]); +impl Version { + /// Version number. + pub fn number(&self) -> u8 { + self.0[3] + } +} + impl wire::Encode for Version { fn encode(&self, writer: &mut W) -> Result { - writer.write_all(&PROTOCOL_VERSION.0)?; + writer.write_all(&PROTOCOL_VERSION_STRING.0)?; - Ok(PROTOCOL_VERSION.0.len()) + Ok(PROTOCOL_VERSION_STRING.0.len()) } } @@ -32,7 +41,7 @@ impl wire::Decode for Version { let mut version = [0u8; 4]; reader.read_exact(&mut version[..])?; - if version != PROTOCOL_VERSION.0 { + if version != PROTOCOL_VERSION_STRING.0 { return Err(wire::Error::InvalidProtocolVersion(version)); } Ok(Self(version)) @@ -197,7 +206,7 @@ impl Frame { /// Create a 'git' protocol frame. pub fn git(stream: StreamId, data: Vec) -> Self { Self { - version: PROTOCOL_VERSION, + version: PROTOCOL_VERSION_STRING, stream, data: FrameData::Git(data), } @@ -206,7 +215,7 @@ impl Frame { /// Create a 'control' protocol frame. pub fn control(link: Link, ctrl: Control) -> Self { Self { - version: PROTOCOL_VERSION, + version: PROTOCOL_VERSION_STRING, stream: StreamId::control(link), data: FrameData::Control(ctrl), } @@ -215,7 +224,7 @@ impl Frame { /// Create a 'gossip' protocol frame. pub fn gossip(link: Link, msg: Message) -> Self { Self { - version: PROTOCOL_VERSION, + version: PROTOCOL_VERSION_STRING, stream: StreamId::gossip(link), data: FrameData::Gossip(msg), } @@ -306,6 +315,9 @@ impl wire::Encode for Control { impl wire::Decode for Frame { fn decode(reader: &mut R) -> Result { let version = Version::decode(reader)?; + if version.number() > PROTOCOL_VERSION { + return Err(wire::Error::UnknownProtocolVersion(version.number())); + } let stream = StreamId::decode(reader)?; match stream.kind() { diff --git a/radicle-node/src/wire/protocol.rs b/radicle-node/src/wire/protocol.rs index 2d35f526..18b8ef98 100644 --- a/radicle-node/src/wire/protocol.rs +++ b/radicle-node/src/wire/protocol.rs @@ -31,6 +31,7 @@ use crate::service; use crate::service::io::Io; use crate::service::FETCH_TIMEOUT; use crate::service::{session, DisconnectReason, Metrics, Service}; +use crate::wire; use crate::wire::frame; use crate::wire::frame::{Frame, FrameData, StreamId}; use crate::wire::Encode; @@ -812,6 +813,15 @@ where // Buffer is empty, or message isn't complete. break; } + Err(wire::Error::UnknownProtocolVersion(v)) => { + // It's ok for a peer to send frames from a newer protocol version + // that we don't understand. We just ignore them. + log::debug!( + target: "wire", + "Ignoring frame with newer protocol version ({v})", + ); + continue; + } Err(e) => { log::error!(target: "wire", "Invalid gossip message from {nid}: {e}"); @@ -1199,7 +1209,7 @@ mod test { let pong = Message::Pong { zeroes: ZeroBytes::new(42), }; - frame::PROTOCOL_VERSION.encode(&mut stream).unwrap(); + frame::PROTOCOL_VERSION_STRING.encode(&mut stream).unwrap(); frame::StreamId::gossip(Link::Outbound) .encode(&mut stream) .unwrap();