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.
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:
| Field | Type | Description |
|---|---|---|
name | string | User-chosen node name. Must be unique inside the config file. |
type | string | Must be exactly "Disturber". |
next | string | Required 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:
| Setting | Type | Default | Description |
|---|---|---|---|
disturb-upstream | boolean | true | Enables disturbance on upstream init and upstream payload. |
disturb-downstream | boolean | false | Enables disturbance on downstream init and downstream payload. |
Chance fields:
| Setting | Type | Default | Description |
|---|---|---|---|
chance_instant_close | integer percent | 0 | Chance to close when init arrives for an enabled direction. |
chance_middle_close | integer percent | 0 | Chance to close while handling an enabled-direction payload. |
chance_payload_corruption | integer percent | 0 | Chance to mutate bytes in the current payload before forwarding. |
chance_payload_loss | integer percent | 0 | Chance to drop the current payload. |
chance_payload_duplication | integer percent | 0 | Chance to create one extra copy of the payload. |
chance_payload_out_of_order | integer percent | 0 | Chance to hold the current payload and release it around a later payload. |
chance_payload_delay | integer percent | 0 | Chance to delay the current payload. |
chance_connection_deadhang | integer percent | 0 | Chance to put the direction into dead-hang mode. |
Delay fields:
| Setting | Type | Default | Description |
|---|---|---|---|
delay_min_ms | integer | 0 | Minimum delay when payload delay triggers. Negative values are clamped to 0. |
delay_max_ms | integer | 0 | Maximum 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:
| Direction | Forwarding callback |
|---|---|
| upstream payload | tunnelNextUpStreamPayload |
| downstream payload | tunnelPrevDownStreamPayload |
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:
- If the direction is already dead-hung, the payload is dropped.
chance_middle_closechance_payload_losschance_payload_duplicationchance_payload_corruption- previously held out-of-order payload release
chance_payload_out_of_orderchance_payload_delaychance_connection_deadhang- 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 = 100is 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.