diff --git a/controller/src/controller.rs b/controller/src/controller.rs index db6ca1481..8ec15314e 100644 --- a/controller/src/controller.rs +++ b/controller/src/controller.rs @@ -104,6 +104,8 @@ impl Controller { None, ms_monotonic(), |packet| -> Result<(), OutOfBoundsError> { + let payload_start = packet.len(); + if let Some(in_re_message_id) = in_re_message_id { let ok_header = packet.append_struct_get_mut::()?; ok_header.verb = protocol::verbs::VL1_OK; @@ -131,12 +133,16 @@ impl Controller { packet.append_u16(config_data.len() as u16)?; packet.append_bytes(config_data.as_slice())?; - // TODO: compress - // NOTE: V1 supports a bunch of other things like chunking but it was never truly used and is optional. - // Omit it here as it adds overhead. + // Omit it here as it adds overhead and requires an extra signature we don't need since the other side + // knows this packet is coming directly from the controller. This stuff was originally designed to support + // a scatter-gather method of config distribution that was never implemented. V2 will just KISS and do + // controller clustering instead if we need scalability or more fault tolerance. } + let new_payload_len = protocol::compress(&mut packet.as_bytes_mut()[payload_start..]); + packet.set_size(payload_start + new_payload_len); + Ok(()) }, ); @@ -254,7 +260,7 @@ impl Controller { let mut nc = NetworkConfig::new(network_id, source_identity.address); - nc.name = member.name.clone(); + nc.name = network.name.clone(); nc.private = network.private; nc.timestamp = now; nc.credential_ttl = credential_ttl; @@ -419,7 +425,7 @@ impl InnerProtocol for Controller { let (result, config) = match self2.get_network_config(&peer.identity, network_id, now).await { Result::Ok((result, Some(config), revocations)) => { - dump_network_config(&config); + //println!("{}", serde_yaml::to_string(&config).unwrap()); self2.send_network_config(peer.as_ref(), &config, Some(message_id)); (result, Some(config)) } @@ -505,7 +511,3 @@ impl Drop for Controller { } } } - -fn dump_network_config(nc: &NetworkConfig) { - println!("{}", serde_yaml::to_string(nc).unwrap()); -} diff --git a/network-hypervisor/src/protocol.rs b/network-hypervisor/src/protocol.rs index fa1efbbcb..9d0425052 100644 --- a/network-hypervisor/src/protocol.rs +++ b/network-hypervisor/src/protocol.rs @@ -12,6 +12,8 @@ use zerotier_crypto::aes_gmac_siv::AesGmacSiv; use zerotier_crypto::hash::hmac_sha384; use zerotier_crypto::secret::Secret; +use self::v1::VERB_FLAG_COMPRESSED; + /* * Legacy V1 protocol versions: * @@ -518,6 +520,27 @@ pub mod v1 { } } +/// Compress a packet payload, returning the new size of the payload or the same size if unchanged. +/// +/// This also sets the VERB_FLAG_COMPRESSED flag in the verb, which is assumed to be the first byte. +/// If compression fails for some reason or does not yield a result that is actually smaller, the +/// buffer is left unchanged and its size is returned. If compression succeeds the buffer's data after +/// the first byte (the verb) is rewritten with compressed data and the new size of the payload +/// (including the verb) is returned. +pub fn compress(payload: &mut [u8]) -> usize { + if payload.len() > 32 { + let mut tmp: [u8; 65536] = unsafe { MaybeUninit::uninit().assume_init() }; + if let Ok(mut compressed_size) = lz4_flex::block::compress_into(&payload[1..], &mut tmp) { + if compressed_size < (payload.len() - 1) { + payload[0] |= VERB_FLAG_COMPRESSED; + payload[1..(1 + compressed_size)].copy_from_slice(&tmp[..compressed_size]); + return 1 + compressed_size; + } + } + } + return payload.len(); +} + #[derive(Clone, Copy)] #[repr(C, packed)] pub struct OkHeader { diff --git a/utils/src/dictionary.rs b/utils/src/dictionary.rs index 192398da6..d0490647b 100644 --- a/utils/src/dictionary.rs +++ b/utils/src/dictionary.rs @@ -16,12 +16,9 @@ const BOOL_TRUTH: &str = "1tTyY"; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Dictionary(pub(crate) BTreeMap>); -fn write_escaped(b: &[u8], w: &mut W) -> std::io::Result<()> { - let mut i = 0_usize; - let l = b.len(); - while i < l { - let ii = i + 1; - match b[i] { +fn write_escaped(mut b: &[u8], w: &mut W) -> std::io::Result<()> { + while !b.is_empty() { + match b[0] { 0 => { w.write_all(&[b'\\', b'0'])?; } @@ -38,10 +35,10 @@ fn write_escaped(b: &[u8], w: &mut W) -> std::io::Result<()> { w.write_all(&[b'\\', b'\\'])?; } _ => { - w.write_all(&b[i..ii])?; + w.write_all(&b[..1])?; } } - i = ii; + b = &b[1..]; } Ok(()) } @@ -128,7 +125,6 @@ impl Dictionary { ); } - /// Write a dictionary in transport format to a writer. pub fn write_to(&self, w: &mut W) -> std::io::Result<()> { for kv in self.0.iter() { write_escaped(kv.0.as_bytes(), w)?; @@ -139,14 +135,12 @@ impl Dictionary { Ok(()) } - /// Write a dictionary in transport format to a byte vector. pub fn to_bytes(&self) -> Vec { let mut b: Vec = Vec::with_capacity(32 * self.0.len()); let _ = self.write_to(&mut b); b } - /// Decode a dictionary in byte format, or return None if the input is invalid. pub fn from_bytes(b: &[u8]) -> Option { let mut d = Dictionary::new(); let mut kv: [Vec; 2] = [Vec::new(), Vec::new()];