Skip to main content

PacketSender

PacketSender is a layer-3 IPv4 packet generator. It pre-builds raw IPv4 packets at startup and sends them across WaterWall worker packet lines over a configured duration.

It is mainly useful for controlled packet-path testing. Paired with PacketReceiver, it can help find good, reachable, or whitelisted source IPs in IP-restricted environments: generate packets from candidate source ranges, send them through the packet path, then compare the receiver report to see which source IPs arrived.

warning

Only use this on networks and address ranges you are authorized to test. Source-IP experiments also depend on the sender-side network. If your provider, kernel path, raw socket backend, or upstream network blocks non-local source addresses, the generated packets may never leave the sender.

Typical Placement

Raw IPv4 output:

PacketSender -> RawSocket

With packet manipulation:

PacketSender -> IpManipulator -> RawSocket

WireGuard-style packet transport:

PacketSender -> WireGuardDevice -> UdpStatelessSocket

Matching receiver side:

RawSocket -> PacketReceiver

PacketSender is a chain-head singleton adapter. It must be at the start of a layer-3 chain and must have a next node.

What It Does

  • Accepts one IPv4 source CIDR range or an array of source CIDR ranges.
  • Accepts one destination IPv4 address.
  • Builds raw IPv4 packets for TCP, UDP, ICMP, or ALL.
  • Repeats each source IP packets-per-ip times.
  • Materializes the whole packet store before sending starts.
  • Distributes packet indexes across workers.
  • Sends packets over duration-ms, using packet index deadlines to spread them across the configured duration.
  • Updates the packet line routing context before each send.
  • Does not close or finish worker packet lines after sending.
  • Stops a worker's timer and send loop if the downstream side finishes that worker packet line.

Source ranges are processed in config order. They are not interleaved.

Source-IP Allowlist Testing

PacketSender and PacketReceiver are built for scenarios where the path or service is restricted by source IP. A common workflow is:

  1. Configure PacketSender with candidate source-ipv4-range values.
  2. Configure PacketReceiver with the same ranges.
  3. Send packets toward the receiver-side host or packet chain.
  4. Read the receiver report.
  5. Treat source IPs with high or complete delivery as the better candidates.

For single-protocol modes, set PacketReceiver.expected-packets-per-ip to the sender packets-per-ip. For protocol-number: "ALL", set it to packets-per-ip * 255.

Configuration Example

ICMP probing:

{
"name": "packet-sender",
"type": "PacketSender",
"settings": {
"source-ipv4-range": [
"198.51.100.0/24",
"203.0.113.0/24"
],
"dest-ipv4": "203.0.113.20",
"protocol-number": "ICMP",
"duration-ms": 5000,
"packets-per-ip": 100
},
"next": "raw-ip"
}

TCP-shaped probing:

{
"name": "packet-sender",
"type": "PacketSender",
"settings": {
"source-ipv4-range": "198.51.100.0/24",
"dest-ipv4": "203.0.113.20",
"protocol-number": "TCP",
"duration-ms": 5000,
"packets-per-ip": 10,
"dest-port": 443,
"src-port": "random"
},
"next": "raw-ip"
}

Required Fields

Top-level fields:

FieldTypeDescription
namestringUser-chosen node name. Must be unique inside the config file.
typestringMust be exactly "PacketSender".
settingsobjectRequired and must not be empty.
nextstringRequired. The next layer-3 node receives generated packets.

Required settings fields:

FieldTypeDescription
source-ipv4-rangestring or array of stringsOne IPv4 CIDR range, or an array of IPv4 CIDR ranges.
dest-ipv4stringDestination IPv4 address. Must be a single IPv4 address, not CIDR.
protocol-numberstringTCP, UDP, ICMP, or ALL. Case-insensitive.
duration-msintegerPositive send duration in milliseconds.
dest-portintegerRequired for TCP, UDP, and ALL. Must be non-zero and in 0..65535.
src-portinteger or "random"Required for TCP, UDP, and ALL. Must be non-zero, or "random".

dest-port and src-port are not required for ICMP.

Optional Settings

SettingTypeDefaultDescription
packets-per-ipinteger1Positive repeat count for each source IP. In ALL mode, each repeat sends 255 protocol variants.

When src-port is "random", the current implementation selects a stable ephemeral-range port from 49152..65535 based on source IP, destination IP, and protocol. It is deterministic for the generated packet shape rather than random per packet.

Generated Packet Shapes

All generated packets are IPv4 with:

FieldValue
TTL64
fragment flagDon't Fragment (DF)
source IPone address from source-ipv4-range
destination IPdest-ipv4
checksumscalculated before materialization

Protocol-specific shapes:

protocol-numberGenerated packet
TCPMinimal TCP segment with ACK flag, deterministic sequence/ack numbers, configured ports, and valid checksums.
UDPMinimal UDP datagram with configured ports and valid checksums.
ICMPICMP echo request with deterministic identifier/sequence and valid checksums.
ALLFor every source IP and repeat, one packet for protocol numbers 0 through 254. Protocols 6, 17, and 1 are valid TCP, UDP, and ICMP shapes; all others are IPv4 packets with an 8-byte opaque payload.

ALL mode emits 255 protocol variants per repeat, not 256.

Scheduling And Workers

Before runtime starts, PacketSender materializes every packet into a contiguous packet store. During startup:

  • total packet indexes are divided across workers
  • each worker uses its persistent worker packet line
  • each packet gets a global deadline based on its index and duration-ms
  • workers send ready packets and re-arm one-shot timers when the next packet is not due yet

This spreads packets across the configured duration instead of sending the whole store as one immediate burst. If several packets are due at the same moment, a worker may send them back-to-back.

Limits

  • Total source IP count must fit in uint32_t.
  • Total packet count must fit in uint64_t.
  • Total materialized packet bytes must fit in size_t.
  • The materialized packet store is capped at 8 GiB.
  • required_padding_left = 0.

Large ranges, ALL mode, and high packets-per-ip values grow memory usage quickly. Split large tests into smaller ranges or several runs if needed.

Packet-Line Semantics

PacketSender uses worker packet lines created by the chain. These packet lines are persistent worker helper lines, not per-connection lines.

PacketSender does not destroy packet lines and does not treat Finish as successful completion. A downstream Finish permanently stops that worker's timer and send loop, while normal completion only logs after all assigned packets were sent.

Practical Notes

  • Use PacketReceiver on the target side to get per-source delivery counts.
  • Choose report-after-ms on PacketReceiver longer than sender duration-ms plus expected network delay and reordering time.
  • Use ICMP for simple path probing when allowed.
  • Use TCP, UDP, or ALL when the restricted environment behaves differently by protocol or port.
  • If the sender-side network enforces source address validation, spoofed or non-local source ranges may be filtered before reaching WaterWall's peer.