Files
nym/nym-node/src/node/lp/data/handler.rs
T
Simon Wicky 71d4b5b3ea moving lp packets in lp-data crate (#6810)
* moving lp packets in lp-data crate

* one more bit

* fmt

* crate description
2026-05-20 14:32:01 +02:00

160 lines
6.0 KiB
Rust

// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
//! LP Data Handler - UDP listener for LP data plane (port 51264)
//!
//! This module handles the data plane for LP clients that have completed registration
//! via the control plane (TCP:41264). LP-wrapped Sphinx packets arrive here, get
//! decrypted, and are forwarded into the mixnet.
//!
//! # Packet Flow
//!
//! ```text
//! LP Client → UDP:51264 → LP Data Handler → Mixnet Entry
//! LP(Sphinx) decrypt LP forward Sphinx
//! ```
//!
use crate::node::lp::error::LpHandlerError;
use crate::node::lp::state::SharedLpDataState;
use nym_lp_data::packet::OuterHeader;
use nym_metrics::inc;
use std::net::SocketAddr;
use tracing::*;
/// LP Data Handler for UDP data plane
pub struct LpDataHandler {
/// State used for handling received requests
#[allow(dead_code)]
state: SharedLpDataState,
}
impl LpDataHandler {
/// Create a new LP data handler
pub fn new(state: SharedLpDataState) -> Self {
Self { state }
}
/// Handle a single UDP packet
///
/// # Packet Processing Steps
/// 1. Parse LP header to get receiver_idx (for routing)
/// 2. Look up session state machine by receiver_idx
/// 3. Process packet through state machine (handles decryption + replay protection)
/// 4. Forward decrypted Sphinx packet to mixnet
///
/// # Security
/// The state machine's `process_input()` method handles replay protection by:
/// - Checking packet counter against receiving window
/// - Marking counter as used after successful decryption
///
/// This prevents replay attacks where captured packets are re-sent.
pub(crate) async fn handle_packet(
&self,
packet: &[u8],
src_addr: SocketAddr,
) -> Result<(), LpHandlerError> {
inc!("lp_data_packets_received");
let _ = OuterHeader::parse(packet)?;
trace!(
"received {} bytes from {src_addr} on the unimplemented LP Data endpoint",
packet.len()
);
Err(LpHandlerError::UnimplementedDataChannel)
// leave old code for future reference
//
// // Step 1: Parse LP header (always cleartext for routing)
// let header = nym_lp::codec::parse_lp_header_only(packet).map_err(|e| {
// LpHandlerError::LpProtocolError(format!("Failed to parse LP header: {}", e))
// })?;
//
// let receiver_idx = header.receiver_idx;
// let counter = header.counter;
// let len = packet.len();
//
// trace!("LP data packet from {src_addr} (receiver_idx={receiver_idx}, counter={counter}, len={len})");
//
// // Step 2: Look up session state machine by receiver_idx (mutable for state updates)
// let mut state_entry = self
// .state
// .session_states
// .get_mut(&receiver_idx)
// .ok_or_else(|| {
// inc!("lp_data_unknown_session");
// LpHandlerError::LpProtocolError(format!(
// "Unknown session for receiver_idx {receiver_idx}"
// ))
// })?;
//
// // Update last activity timestamp
// state_entry.value().touch();
//
// // Step 3: Get outer AEAD key for packet parsing
// let outer_key = state_entry
// .value()
// .state
// .session()
// .map_err(|e| LpHandlerError::LpProtocolError(format!("Session error: {e}")))?
// .outer_aead_key();
//
// // Parse full packet with outer AEAD decryption
// let lp_packet = nym_lp::codec::parse_lp_packet(packet, Some(outer_key)).map_err(|e| {
// inc!("lp_data_decrypt_errors");
// LpHandlerError::LpProtocolError(format!("Failed to decrypt LP packet: {}", e))
// })?;
//
// // Step 4: Process packet through state machine
// // This handles:
// // - Replay protection (counter check + mark)
// // - Inner Noise decryption
// // - Subsession handling if applicable
// let state_machine = &mut state_entry.value_mut().state;
//
// let action = state_machine
// .process_input(LpInput::ReceivePacket(lp_packet))
// .ok_or_else(|| {
// LpHandlerError::LpProtocolError("State machine returned no action".to_string())
// })?
// .map_err(|e| {
// inc!("lp_data_state_machine_errors");
// LpHandlerError::LpProtocolError(format!("State machine error: {}", e))
// })?;
//
// // Release session lock before forwarding
// drop(state_entry);
//
// // Step 5: Handle the action from state machine
// match action {
// LpAction::DeliverData(data) => {
// // Decrypted application data - forward as Sphinx packet
// self.forward_sphinx_packet(&data.content).await?;
// inc!("lp_data_packets_forwarded");
// Ok(())
// }
// LpAction::SendPacket(_response_packet) => {
// // UDP is connectionless - we can't send responses back easily
// // For subsession rekeying, the client should use TCP control plane
// debug!(
// "Ignoring SendPacket action on UDP (receiver_idx={receiver_idx}) - use TCP for rekeying",
// );
// inc!("lp_data_ignored_send_actions");
// Ok(())
// }
// other => {
// warn!(
// "Unexpected action on UDP data plane from {}: {:?}",
// src_addr, other
// );
// inc!("lp_data_unexpected_actions");
// Err(LpHandlerError::LpProtocolError(format!(
// "Unexpected state machine action on UDP: {:?}",
// other
// )))
// }
// }
}
}