Overview
The Jitter transform adds pseudo-random delays to packet transmission, disrupting temporal traffic patterns that DPI systems use for classification. This helps defeat timing-based traffic analysis and flow fingerprinting.
Structure
pub struct JitterTransform {
params: JitterParams,
}
Configuration
Minimum jitter delay in milliseconds. When min_ms equals max_ms, a fixed delay is applied.
Maximum jitter delay in milliseconds. The actual delay will vary pseudo-randomly between min_ms and max_ms.Must not exceed the safety limit defined in limits.max_jitter_ms (default: 500ms).
Methods
new
pub fn new(params: &JitterParams) -> Self
Creates a new Jitter transform with the specified parameters.
calculate_jitter
fn calculate_jitter(&self, seed: u64) -> Duration
Calculates the jitter delay for the current packet using a seed derived from packet count and data length.
Returns: Duration between min_ms and max_ms.
apply
fn apply(&self, ctx: &mut FlowContext<'_>, data: &mut BytesMut) -> Result<TransformResult>
Applies a jitter delay to the packet.
Returns: TransformResult::Delay if jitter was applied, TransformResult::Continue if max_ms is 0.
Behavior
- If
max_ms is 0, no delay is applied
- Delay is calculated pseudo-randomly based on packet count and data length using:
seed = packet_count * 31337 + data.len()
- The delay is requested via
ctx.request_delay(jitter)
- Statistics are tracked:
last_jitter_ms: The jitter applied to the current packet
total_jitter_ms: Cumulative jitter across all packets in the flow
Example Configuration
Fixed delay
{
"jitter": {
"min_ms": 25,
"max_ms": 25
}
}
Adds exactly 25ms delay to every packet.
Variable delay
{
"jitter": {
"min_ms": 10,
"max_ms": 50
}
}
Adds between 10ms and 50ms delay to each packet.
No jitter
{
"jitter": {
"min_ms": 0,
"max_ms": 0
}
}
Code Example
From jitter.rs:115:
#[test]
fn test_jitter_applied() {
let params = JitterParams {
min_ms: 10,
max_ms: 50,
};
let transform = JitterTransform::new(¶ms);
let key = test_flow_key();
let mut state = FlowState::new(key);
let mut ctx = FlowContext::new(&key, &mut state, None);
let mut data = BytesMut::from(&b"test data"[..]);
let result = transform.apply(&mut ctx, &mut data).unwrap();
assert_eq!(result, TransformResult::Delay);
let delay = ctx.delay.unwrap();
assert!(delay >= Duration::from_millis(10));
assert!(delay <= Duration::from_millis(50));
}
Jitter Calculation
From jitter.rs:22:
fn calculate_jitter(&self, seed: u64) -> Duration {
if self.params.max_ms == 0 {
return Duration::ZERO;
}
let range = self.params.max_ms - self.params.min_ms;
if range == 0 {
return Duration::from_millis(self.params.min_ms);
}
// Pseudo-random jitter based on seed
let jitter_ms = self.params.min_ms + (seed % (range + 1));
Duration::from_millis(jitter_ms)
}
The seed is generated in apply() using:
let seed = ctx.state.packet_count
.wrapping_mul(31337)
.wrapping_add(data.len() as u64);
Validation
From config.rs:90-98:
if self.transforms.jitter.max_ms > self.limits.max_jitter_ms {
return Err(EngineError::validation(
"transforms.jitter.max_ms",
format!(
"exceeds safety limit of {}ms",
self.limits.max_jitter_ms
),
));
}
Jitter has a safety limit (default 500ms) to prevent excessive delays that could cause connection timeouts.
Helper Method
From config.rs:381:
impl JitterParams {
pub fn as_duration_range(&self) -> (Duration, Duration) {
(Duration::from_millis(self.min_ms), Duration::from_millis(self.max_ms))
}
}
Converts millisecond parameters to Duration tuple.
Use Cases
- Timing disruption: Break timing-based traffic fingerprints
- Flow anonymization: Obscure inter-packet timing patterns
- Protocol mimicry: Match timing characteristics of other protocols
- Anti-correlation: Prevent timing correlation attacks
- Jitter delays packet transmission, potentially reducing throughput
- Higher jitter values increase latency
- Very high jitter can cause protocol timeouts (hence the safety limit)
- Consider application requirements when setting jitter values