A small synthetic sFlow v5 generator intended for FastNetMon testing, demonstrations, dashboards, and attack-detection validation.
The generator does not transmit real application traffic. It sends sFlow FlowSample records containing RawPacketFlow data with synthetic Ethernet, IPv4 or IPv6, and TCP, UDP, ICMP, or ICMPv6 headers. FastNetMon can then estimate the represented traffic volume using the configured sampling rate and original frame length.
Warning
This is a synthetic traffic test tool. Review its configuration and validate the resulting telemetry before using it in shared or production-like environments.
This test tool was vibe-coded with OpenAI GPT-5.6 Thinking.
The project requirements and testing direction were provided by the maintainer. The Go implementation, tests, configuration examples, and documentation were generated and iteratively refined through conversations with the model rather than written manually line by line.
- sFlow v5 export over UDP.
- Simultaneous IPv4 and IPv6 background traffic.
- Separate
ambientandattackprocesses. - Infinite ambient generation by default.
- Bidirectional ambient traffic with configurable incoming/outgoing proportions.
- Smooth per-host variation using correlated AR(1) noise.
- Stable but non-uniform top talkers using Zipf-distributed base weights.
- Smooth attack ramp-up, hold, and ramp-down phases.
- TCP, UDP, ICMP, and ICMPv6 packet headers.
- Deterministic generation through a configurable random seed.
- Strict YAML parsing and standalone configuration validation.
- Runtime statistics for target and represented traffic rates.
ambient continuously produces background traffic for one or more internal IPv4 or IPv6 networks.
go run . ambient --config examples/ambient.yamlBy default, ambient mode runs indefinitely until the process receives SIGINT or SIGTERM.
To run it for a limited period, add a duration to the ambient configuration:
duration: "2m"Ambient mode is designed to keep the total network rate relatively stable while allowing individual host rates to move smoothly over time. Incoming and outgoing traffic for the same internal host use the same changing host weight, making the two directions correlated rather than independent.
attack is a separate finite process that adds traffic on top of an optional ambient process. All generated attack traffic is associated with a single victim address.
go run . attack --config examples/attack-ipv4.yaml
go run . attack --config examples/attack-ipv6.yamlThe attack profile consists of:
- smooth ramp-up using a smoothstep curve;
- a constant hold period at
peak_rate; - smooth ramp-down;
- process termination.
Ambient and attack configurations normally use different sub_agent_id values, allowing both processes to use the same sFlow agent address and collector simultaneously.
- Go 1.22 or newer.
- A reachable sFlow collector, such as FastNetMon listening on UDP port 6343.
go mod tidy
make test
make vet
make buildThe resulting binary is created at:
bin/sflowgen
Run it with:
./bin/sflowgen ambient --config examples/ambient.yaml
./bin/sflowgen attack --config examples/attack-ipv4.yamlValidate a configuration without sending traffic:
./bin/sflowgen validate --mode ambient --config examples/ambient.yaml
./bin/sflowgen validate --mode attack --config examples/attack-ipv4.yamlFastNetMon must be configured to receive sFlow from the generator. The internal CIDRs used in the generator should also be present in the FastNetMon monitored networks list.
For example, when the ambient configuration contains:
networks:
- cidr: "10.10.0.0/24"
- cidr: "2001:db8:10::/64"FastNetMon should classify those networks as internal. Addresses selected from peer_networks or source_networks should normally remain external.
The exporter section is shared by ambient and attack modes:
exporter:
collector: "127.0.0.1:6343"
agent_ip: "192.0.2.10"
sub_agent_id: 0
sampling_rate: 4096
tick: "100ms"
samples_per_datagram: 8
seed: 42| Field | Description |
|---|---|
collector |
Destination address of the sFlow collector. |
agent_ip |
Address reported as the sFlow agent. It is independent of the sampled IPv4 or IPv6 packet addresses. |
sub_agent_id |
Logical sub-agent identifier. Use different values for concurrently running ambient and attack processes. |
sampling_rate |
Number of represented packets per generated sample. |
tick |
Internal traffic-model update interval. |
samples_per_datagram |
Maximum number of samples grouped into one sFlow UDP datagram. |
seed |
Random seed used for reproducible runs. |
The generator estimates represented traffic approximately as:
frame_length × sampling_rate × 8
The sFlow sample_pool is incremented by sampling_rate for every generated sample.
Keeping samples_per_datagram around 4 to 10 is generally reasonable for this test tool and helps avoid unnecessarily large UDP datagrams.
packet:
mean_size: 900
stddev: 220
min_size: 64
max_size: 1518Frame lengths are generated from a bounded normal distribution. The minimum size is also adjusted when necessary to fit the selected IP and transport headers.
protocols:
tcp: 0.65
udp: 0.30
icmp: 0.05Protocol values are relative weights and are normalized internally. They do not need to add up to exactly 1.0.
For IPv6 traffic, the ICMP weight produces ICMPv6 headers.
A complete ambient configuration may contain multiple IPv4 and IPv6 networks:
# Omit duration to run indefinitely.
exporter:
collector: "127.0.0.1:6343"
agent_ip: "192.0.2.10"
sub_agent_id: 0
sampling_rate: 4096
tick: "100ms"
samples_per_datagram: 8
seed: 42
packet:
mean_size: 900
stddev: 220
min_size: 64
max_size: 1518
protocols:
tcp: 0.65
udp: 0.30
icmp: 0.05
log_interval: "1s"
max_samples_per_tick: 100000
networks:
- cidr: "10.10.0.0/24"
rate: "2Gbps"
incoming_share: 0.75
outgoing_share: 0.25
hosts: 128
zipf: 1.10
total_noise: 0.015
host_noise: 0.25
correlation: 0.98
peer_networks:
- "198.18.0.0/15"
- cidr: "2001:db8:10::/64"
rate: "1Gbps"
incoming_share: 0.70
outgoing_share: 0.30
hosts: 128
zipf: 1.10
total_noise: 0.015
host_noise: 0.25
correlation: 0.98
peer_networks:
- "2001:db8:ffff::/48"| Field | Description |
|---|---|
cidr |
Internal network represented by this profile. |
rate |
Average combined incoming and outgoing rate for the network. |
incoming_share |
Relative share of traffic entering the internal network. |
outgoing_share |
Relative share of traffic leaving the internal network. |
hosts |
Number of active internal addresses selected from the CIDR. |
zipf |
Skew of the base host distribution. 0 gives equal base weights. |
total_noise |
Variation applied to the total rate of the network. |
host_noise |
Variation applied to individual host shares. |
correlation |
AR(1) persistence. Values around 0.95 to 0.99 produce smooth movement. |
peer_networks |
External networks used to generate remote source and destination addresses. |
The two share values are normalized automatically. Both examples below produce a 75/25 split:
incoming_share: 0.75
outgoing_share: 0.25incoming_share: 3
outgoing_share: 1At least one share must be positive. Set one share to 1 and the other to 0
for single-direction traffic.
Each host receives:
- a stable base weight;
- an optional Zipf rank bias;
- a smoothly changing AR(1) noise component.
Host scores are normalized after every model tick. As a result, individual host rates can change visibly while the total rate remains much more stable.
The same host weight is applied to both incoming and outgoing rates. This creates correlated request/response-like behavior, although the test tool does not maintain real sessions or matching five-tuples.
peer_networks must use the same address family as the corresponding internal cidr.
A peer is selected by first choosing one configured prefix and then choosing a random address within it. Prefix entries currently receive equal selection probability regardless of prefix size.
For example:
peer_networks:
- "198.18.0.0/15"provides roughly 131,000 usable IPv4 peer addresses.
A very broad prefix is technically accepted:
peer_networks:
- "0.0.0.0/0"However, the current implementation does not exclude private, loopback, link-local, multicast, documentation, or other reserved address ranges. Using 0.0.0.0/0 can therefore produce peers that FastNetMon classifies unexpectedly. Explicit external test ranges are safer and more reproducible.
Example IPv4 UDP flood:
exporter:
collector: "127.0.0.1:6343"
agent_ip: "192.0.2.10"
sub_agent_id: 1
sampling_rate: 4096
tick: "100ms"
samples_per_datagram: 8
seed: 1001
packet:
mean_size: 900
stddev: 220
min_size: 64
max_size: 1518
protocols:
tcp: 0
udp: 1
icmp: 0
victim: "10.10.0.42"
direction: incoming
peak_rate: "15Gbps"
ramp_up: "20s"
hold: "3m"
ramp_down: "30s"
source_networks:
- "198.18.0.0/15"
destination_port: 443
tcp_flags: syn
log_interval: "1s"
max_samples_per_tick: 100000| Field | Description |
|---|---|
victim |
Internal IPv4 or IPv6 address receiving or originating the attack traffic. |
direction |
incoming or outgoing, from the perspective of the internal victim. |
peak_rate |
Maximum represented attack rate. |
ramp_up |
Time spent increasing from zero to peak_rate. |
hold |
Time spent at peak_rate. |
ramp_down |
Time spent decreasing from peak_rate to zero. |
source_networks |
Remote networks used for generated peers. They must match the victim address family. |
destination_port |
Transport destination port used in generated TCP or UDP headers. |
tcp_flags |
syn, ack, or syn-ack. Used when TCP is selected. |
A TCP SYN flood profile can be represented as:
protocols:
tcp: 1
udp: 0
icmp: 0
tcp_flags: synAn ICMP or ICMPv6 flood can be represented as:
protocols:
tcp: 0
udp: 0
icmp: 1Ambient example:
mode=ambient target=3.01Gbps target_in=2.25Gbps target_out=758.00Mbps estimated=3.00Gbps estimated_in=2.24Gbps estimated_out=760.00Mbps samples=102 datagrams=13 top=10.10.0.17@402.10Mbps
Attack example:
mode=attack victim=10.10.0.42 target=9.62Gbps estimated=9.59Gbps samples=326 datagrams=41 phase=ramp-up
target is the traffic rate requested by the model. estimated is the traffic volume represented by the emitted samples, not the actual bandwidth consumed by the generator's UDP datagrams.
max_samples_per_tick limits the amount of work performed during a single model update:
max_samples_per_tick: 100000A configuration with a very high represented rate, a low sampling rate, small frames, or a very short tick can reach this limit. The generator then emits a warning and caps the generated sample count for that tick.
- This is a sampled-header generator, not a real packet or application traffic generator.
- No payload data is generated. The represented original size is carried in
FrameLength. - Transport checksums are minimal or synthetic because the expected consumer parses headers rather than forwarding the packets through a network stack.
- There is no stateful TCP, UDP, or request/response session model.
- Incoming and outgoing ambient traffic is correlated by host weight but not matched by five-tuple.
- There is no VLAN tagging, fragmentation, tunneled traffic, counter samples, PCAP replay, or BGP metadata.
- Peer-prefix selection is uniform by configured prefix, not weighted by the number of addresses in each prefix.
- Reserved or non-public addresses are not automatically excluded from broad peer prefixes.
- The pinned
github.com/Cistern/sflowversion has reversed source-ID bit-field handling during encoding.exporter.gocontains a local workaround for data-source interface indexes 1 and 2. Recheck this workaround before updating the dependency.
The source code is licensed under the Apache License 2.0.
Third-party dependencies retain their respective licenses. Required notices, including the BSD 3-Clause notice for github.com/Cistern/sflow, are included in THIRD_PARTY_NOTICES.md.