Skip to main content

Disturber

Disturber is a test tunnel for simulating bad network behavior inside a WaterWall chain. It can inject closes, payload loss, corruption, duplication, out-of-order delivery, delay, and dead-hang behavior without relying on external network tools.

Use it to stress tunnels that should survive unstable paths, such as mux, TCP-over-UDP, HTTP, TLS, packet transports, reconnect logic, and custom tunnels.

warning

Disturber intentionally damages traffic. It is meant for testing, debugging, and resilience validation, not normal production forwarding.

Typical Placement

Simple pass-through test shape:

TesterClient -> Disturber -> TesterServer

Around a transport under test:

TesterClient -> TunnelUnderTest -> Disturber -> PeerUnderTest -> TesterServer

On both directions of a paired tunnel:

TesterClient -> Disturber -> TunnelClient -> ... -> TunnelServer -> Disturber -> TesterServer

Disturber has no socket of its own. It needs a next node and is normally placed in the middle of a stream or packet-capable chain.

What It Does

  • Forwards lifecycle callbacks directionally when no close-like disturbance is triggered.
  • Can close normal lines immediately during init.
  • Can close normal lines during payload forwarding.
  • Can drop payload.
  • Can duplicate payload.
  • Can corrupt payload bytes.
  • Can hold one payload and release it later to simulate reordering.
  • Can delay payload by a random time inside a configured range.
  • Can put one direction into a dead-hang state where payload is silently discarded.
  • Preserves packet-line lifetime by using dead-hang behavior instead of destroying worker packet lines.

All chance fields default to 0, so a default Disturber behaves like a transparent pass-through node except for the default direction settings.

Configuration Example

{
"name": "disturber",
"type": "Disturber",
"settings": {
"disturb-upstream": true,
"disturb-downstream": true,
"chance_instant_close": 5,
"chance_middle_close": 2,
"chance_payload_corruption": 3,
"chance_payload_loss": 10,
"chance_payload_duplication": 2,
"chance_payload_out_of_order": 4,
"chance_payload_delay": 15,
"chance_connection_deadhang": 1,
"delay_min_ms": 50,
"delay_max_ms": 300
},
"next": "next-node"
}

Packet-loss-only example:

{
"name": "loss",
"type": "Disturber",
"settings": {
"disturb-upstream": true,
"disturb-downstream": false,
"chance_payload_loss": 100
},
"next": "receiver"
}

Required Fields

Top-level fields:

FieldTypeDescription
namestringUser-chosen node name. Must be unique inside the config file.
typestringMust be exactly "Disturber".
nextstringRequired for useful operation. The next node receives upstream forwarding.

settings is optional. If it is omitted, all chance values use their defaults.

Optional Settings

Direction controls:

SettingTypeDefaultDescription
disturb-upstreambooleantrueEnables disturbance on upstream init and upstream payload.
disturb-downstreambooleanfalseEnables disturbance on downstream init and downstream payload.

Chance fields:

SettingTypeDefaultDescription
chance_instant_closeinteger percent0Chance to close when init arrives for an enabled direction.
chance_middle_closeinteger percent0Chance to close while handling an enabled-direction payload.
chance_payload_corruptioninteger percent0Chance to mutate bytes in the current payload before forwarding.
chance_payload_lossinteger percent0Chance to drop the current payload.
chance_payload_duplicationinteger percent0Chance to create one extra copy of the payload.
chance_payload_out_of_orderinteger percent0Chance to hold the current payload and release it around a later payload.
chance_payload_delayinteger percent0Chance to delay the current payload.
chance_connection_deadhanginteger percent0Chance to put the direction into dead-hang mode.

Delay fields:

SettingTypeDefaultDescription
delay_min_msinteger0Minimum delay when payload delay triggers. Negative values are clamped to 0.
delay_max_msinteger0Maximum delay when payload delay triggers. Values below delay_min_ms are clamped up to delay_min_ms.

Probability values are interpreted by roll100: values <= 0 never trigger, values >= 100 always trigger, and values from 1 through 99 act as percent chances.

Direction Behavior

By default, only upstream payload is disturbed:

{
"disturb-upstream": true,
"disturb-downstream": false
}

Set disturb-downstream to true when you also want to damage the response path. Set disturb-upstream to false when only downstream traffic should be disturbed.

If a direction is disabled, payload in that direction is forwarded directly:

DirectionForwarding callback
upstream payloadtunnelNextUpStreamPayload
downstream payloadtunnelPrevDownStreamPayload

Est, Pause, and Resume are forwarded normally in both directions.

Payload Pipeline

For each payload in an enabled direction, Disturber evaluates disturbance checks in this order:

  1. If the direction is already dead-hung, the payload is dropped.
  2. chance_middle_close
  3. chance_payload_loss
  4. chance_payload_duplication
  5. chance_payload_corruption
  6. previously held out-of-order payload release
  7. chance_payload_out_of_order
  8. chance_payload_delay
  9. chance_connection_deadhang
  10. normal forwarding

Some checks stop the pipeline immediately. For example, payload loss drops the buffer and returns. Duplication can combine with later checks because it creates an extra buffer before the current payload continues through the rest of the pipeline.

Disturbance Details

Instant Close

When upstream init arrives and disturb-upstream is enabled, chance_instant_close may close the previous side instead of forwarding init to next.

When downstream init arrives and disturb-downstream is enabled, chance_instant_close may close the next side instead of forwarding init to the previous node.

Middle Close

When chance_middle_close triggers on a normal line, the current payload is dropped, local Disturber line state is destroyed, and both directions are finished.

Payload Loss

When chance_payload_loss triggers, the current payload buffer is recycled and not forwarded.

Duplication

When chance_payload_duplication triggers, Disturber creates one duplicate buffer. The duplicate is scheduled for forwarding in the same direction. Later checks may still affect the original payload.

Corruption

When chance_payload_corruption triggers, random bytes in the current payload are XOR-modified. For non-empty payloads, the implementation corrupts up to roughly 10 percent of the payload, with at least one byte modified.

Out-of-Order Delivery

Disturber stores at most one held payload per line per direction.

When out-of-order triggers and there is no held payload, the current payload is stored and not forwarded immediately. When a later payload arrives while one is held, the held payload is released around the current payload to create a simple reordering effect.

If the line state is destroyed while a payload is still held, the held buffer is recycled.

Delay

When chance_payload_delay triggers, a delay is chosen randomly between delay_min_ms and delay_max_ms, inclusive. The payload is scheduled to be forwarded later on the same line.

Dead-Hang

When chance_connection_deadhang triggers, the direction is marked dead-hung. The current payload is dropped, the line remains structurally present, and later payload in that direction is silently discarded.

This simulates a connection that still appears alive but stops making progress.

Packet-Line Behavior

Disturber is packet-line aware.

For normal connection lines, close-like disturbances can propagate Finish and destroy this tunnel's per-line state. For worker packet lines, Disturber does not destroy the packet line during runtime. Instead, instant-close and middle-close behavior mark the affected direction as dead-hung and continue without calling lineDestroy().

This keeps WaterWall packet-line lifetime rules intact while still allowing packet loss and packet blackhole scenarios to be tested.

Notes and Caveats

  • Combining several high chance values can create extremely chaotic traffic.
  • chance_payload_duplication = 100 is useful for testing duplicate-tolerant protocols, but it can multiply traffic quickly.
  • Delay and out-of-order behavior use scheduled callbacks, so they can expose re-entrancy and lifetime bugs in surrounding tunnels.
  • The node adds no framing and has required_padding_left = 0.
  • The API callback currently only accepts and recycles API messages; it does not expose runtime tuning.