132 lines
4.4 KiB
Rust
132 lines
4.4 KiB
Rust
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
//! Wire wrapping / unwrapping
|
|
//!
|
|
//! Everything below the application-message layer (LP packet encode/decode and
|
|
//! fragment reassembly) is identical between roles, so it lives here. The
|
|
//! per-role pipelines embed a [`WirePipeline`] and delegate their
|
|
//! [`Framing`]/[`Transport`]/[`*Unwrap`] trait methods to it.
|
|
//!
|
|
//! [`Framing`]: nym_lp_data::common::traits::Framing
|
|
//! [`Transport`]: nym_lp_data::common::traits::Transport
|
|
|
|
use std::{net::SocketAddr, sync::Arc, time::Instant};
|
|
|
|
use nym_lp_data::{
|
|
AddressedTimedData, TimedData,
|
|
fragmentation::fragment::fragment_lp_message,
|
|
packet::{
|
|
EncryptedLpPacket, LpFrame, LpHeader, LpPacket, MalformedLpPacketError, frame::LpFrameKind,
|
|
version,
|
|
},
|
|
};
|
|
use rand::Rng;
|
|
use tracing::warn;
|
|
|
|
use crate::node::lp::data::shared::SharedLpDataState;
|
|
|
|
/// Wire-layer pipeline: handles LP packet encode/decode and fragment
|
|
/// reassembly. Has no knowledge of the application message type carried in
|
|
/// the frame.
|
|
pub struct WirePipeline<R> {
|
|
state: Arc<SharedLpDataState>,
|
|
rng: R,
|
|
}
|
|
|
|
impl<R: Rng> WirePipeline<R> {
|
|
pub fn new(state: Arc<SharedLpDataState>, rng: R) -> Self {
|
|
Self { state, rng }
|
|
}
|
|
|
|
/// Wrap an [`LpFrame`] into an [`EncryptedLpPacket`] for the wire.
|
|
pub fn frame_to_packet(
|
|
&mut self,
|
|
frame: AddressedTimedData<LpFrame>,
|
|
) -> AddressedTimedData<EncryptedLpPacket> {
|
|
// Here be LP encryption. For now, just wrap into an EncryptedLpPacket; we don't care at reception anyway
|
|
frame.data_transform(|f| LpPacket::new(LpHeader::new(0, 0, version::CURRENT), f).encode())
|
|
}
|
|
|
|
/// Unwrap an [`EncryptedLpPacket`] off the wire into an [`LpFrame`].
|
|
pub fn packet_to_frame(
|
|
&mut self,
|
|
packet: EncryptedLpPacket,
|
|
timestamp: Instant,
|
|
) -> Result<TimedData<LpFrame>, MalformedLpPacketError> {
|
|
// Here be LP decryption. For now we do as is, it's not encrypted
|
|
let lp_packet = LpPacket::decode(packet).inspect_err(|_| {
|
|
self.state.malformed_packet();
|
|
})?;
|
|
Ok(TimedData {
|
|
timestamp,
|
|
data: lp_packet.into_frame(),
|
|
})
|
|
}
|
|
|
|
/// Wrap an [`LpFrame`] into one or more addressed frames, fragmenting it
|
|
/// if its serialized length would exceed `frame_size`.
|
|
pub fn message_to_frame(
|
|
&mut self,
|
|
timestamp: Instant,
|
|
frame: LpFrame,
|
|
dst: SocketAddr,
|
|
frame_size: usize,
|
|
) -> Vec<AddressedTimedData<LpFrame>> {
|
|
let output_frames = if frame.len() > frame_size {
|
|
fragment_lp_message(&mut self.rng, frame, frame_size)
|
|
.into_iter()
|
|
.map(|f| f.into_lp_frame())
|
|
.collect()
|
|
} else {
|
|
vec![frame]
|
|
};
|
|
|
|
output_frames
|
|
.into_iter()
|
|
.map(|f| AddressedTimedData::new_addressed(timestamp, f, dst))
|
|
.collect()
|
|
}
|
|
|
|
/// If the frame carries a fragment, attempt reassembly; otherwise return
|
|
/// the frame as-is. Returns `None` when more fragments are needed or
|
|
/// reassembly fails. The returned frame is guaranteed not to be a
|
|
/// fragment.
|
|
pub fn frame_to_maybe_message(
|
|
&mut self,
|
|
frame: TimedData<LpFrame>,
|
|
) -> Option<TimedData<LpFrame>> {
|
|
let reassembled = if frame.data.kind() == LpFrameKind::FragmentedData {
|
|
let fragment = frame
|
|
.data
|
|
.try_into()
|
|
.inspect_err(|e| {
|
|
tracing::error!("Failed to recover a fragment : {e}");
|
|
self.state.malformed_packet();
|
|
})
|
|
.ok()?;
|
|
let message = self
|
|
.state
|
|
.message_reconstructor
|
|
.insert_new_fragment(fragment, frame.timestamp)?
|
|
.inspect_err(|e| {
|
|
tracing::error!("Failed to recover a frame : {e}");
|
|
self.state.malformed_packet();
|
|
})
|
|
.ok()?;
|
|
TimedData::new(frame.timestamp, message)
|
|
} else {
|
|
frame
|
|
};
|
|
|
|
if reassembled.data.kind() == LpFrameKind::FragmentedData {
|
|
warn!(
|
|
"Fragmented data inside fragmented data, it shouldn't happen. Dropping the message"
|
|
);
|
|
None
|
|
} else {
|
|
Some(reassembled)
|
|
}
|
|
}
|
|
}
|