Tutorial: ARP sweep
Time: about 15 minutes. Privileges: none until Step 5 (everything before it is a dry run).
What you will build
A stream of ARP requests (“who has 10.0.0.x?”) that walks every host address of a /22, and then the same sweep repeated inside several VLANs. ARP sweeps are the classic way to populate an ARP cache, to check that a switch floods broadcasts correctly, and to exercise a router’s ARP glean rate limiting.
You will use three bundled templates:
| Template | Frames | What it shows |
|---|---|---|
templates/examples/arp-sweep.wpg.yaml | 254 | the smallest sweep: one range generator |
templates/scenarios/arp-sweep-slash22.wpg.yaml | 1022 | a CIDR block as the generator |
templates/scenarios/vlan-arp-sweep.wpg.yaml | 1016 | two generators (VLAN x address) in nested mode |
Before you start
Build wpg once (see Install on Linux, macOS or Windows) and run the commands from the repository root. The examples call the binary wpg; use ./target/release/wpg (or target\release\wpg.exe) if it is not on your PATH. tshark is optional but makes the checks below more convincing.
Only send on networks you own or are authorised to test; see the legal notice.
Step 1. Read the smallest sweep
layers:
- protocol: ethernet2
fields:
dst: ff:ff:ff:ff:ff:ff
src: ${src_mac}
- protocol: arp
fields:
oper: request
sha: ${src_mac}
spa: ${src_ip}
tha: 00:00:00:00:00:00
generators:
tpa: ${first}-${last} # shorthand for a range generator
Two things do the work. generators.tpa varies the ARP target protocol address across the stream, and ${...} placeholders are template variables (declared in the variables: list with defaults) that you can override at run time with --var name=value. ethertype in the Ethernet layer is not written: it is Auto and becomes 0x0806 because the next layer is ARP.
Print the first frame:
$ wpg template show templates/scenarios/vlan-arp-sweep.wpg.yaml
Hex dump (60 bytes)
0000 ff ff ff ff ff ff 02 00 00 00 00 01 81 00 00 64 ...............d
0010 08 06 00 01 08 00 06 04 00 01 02 00 00 00 00 01 ................
0020 0a 00 00 fa 00 00 00 00 00 00 0a 00 00 01 00 00 ................
0030 00 00 00 00 00 00 00 00 00 00 00 00 ................
...
The last 14 octets are zero padding: an ARP request is 42 octets long and Ethernet needs 60 (the FCS is added by the NIC).
Step 2. Sweep a whole /22
arp-sweep-slash22.wpg.yaml swaps the range for a CIDR block held in a variable:
variables:
- name: net
default: 10.0.0.0/22
layers:
- protocol: arp
generators:
tpa: ${net}
A CIDR generator yields the host addresses only, so a /22 gives 1022 frames (10.0.0.1 to 10.0.3.254). Render it to a pcapng file without touching the network:
$ wpg render templates/scenarios/arp-sweep-slash22.wpg.yaml -o /tmp/sweep.pcapng
Wrote 1022 packets to /tmp/sweep.pcapng.
Point the same template at another subnet without editing it:
$ wpg render templates/scenarios/arp-sweep-slash22.wpg.yaml -o /tmp/sweep.pcapng --var net=10.1.0.0/22
Step 3. Repeat the sweep in several VLANs
vlan-arp-sweep.wpg.yaml inserts an 802.1Q tag and adds a second generator:
- protocol: dot1q
generators:
vid: ${vids} # default 100-103
- protocol: arp
generators:
tpa: ${first}-${last} # default 10.0.0.1-10.0.0.254
stream:
mode: nested
order: [dot1q.vid, arp.tpa] # slowest first
With two generators you have to say how they combine. zip advances both in lockstep, product is the Cartesian product, and nested is a product with an explicit order: here the VLAN is the slow loop, so all 254 addresses are asked in VLAN 100, then in VLAN 101, and so on: 4 x 254 = 1016 frames. Restrict it to two VLANs with a list:
$ wpg render templates/scenarios/vlan-arp-sweep.wpg.yaml -o /tmp/vlan-sweep.pcapng --var vids=100,200
Wrote 508 packets to /tmp/vlan-sweep.pcapng.
Step 4. Check the frames
$ tshark -r /tmp/vlan-sweep.pcapng -T fields -e vlan.id -e arp.dst.proto_ipv4 | sed -n '1p;254p;255p;$p'
100 10.0.0.1
100 10.0.0.254
200 10.0.0.1
200 10.0.0.254
Also run wpg template lint on any template you edit: it catches unknown fields, out-of-range values and bad generator syntax before you send.
Step 5. Send it
First rehearse with a backend that writes a file (for pcapfile the -i argument is the output path) or discards the frames (null):
$ wpg send templates/scenarios/arp-sweep-slash22.wpg.yaml --backend null -i lo --rate 100kpps
Sending on lo via null: rate 100000pps, burst 1, 1022 packets, 1 worker(s), plan PreRender (...)
state finished (stream exhausted)
packets 1022
bytes 61320
Then send for real on your lab interface. Raw sending needs privileges (see Permissions); run wpg ifaces to pick the interface and wpg doctor if the backend is refused:
$ wpg ifaces
$ wpg send templates/scenarios/vlan-arp-sweep.wpg.yaml -i eth1 --rate 2000pps
The command runs the stream once (1016 frames at 2000 frames per second is about half a second) and prints a summary. Add --count 10000 to loop the sweep, or --duration 30s to run for a fixed time.
Step 6. Read the result
- On the device under test, look at the ARP or neighbour table (
ip neigh,show ip arp) and at the MAC table of the switch (the sender MAC02:00:00:00:00:01should appear on the port and in the VLANs you used). - On a Linux host in the same segment,
tcpdump -e -n -i eth1 arpshows the requests with their VLAN tags. - The sweep uses a made-up sender address (
10.0.0.250). Changesrc_macandsrc_ipwith--varso that replies come back to an address you can observe.
Next steps
- VLAN-tagged TCP SYN flood for random source addresses and TCP options.
- Load and rate control for
--rate,--durationand multi-worker sending. - Template schema for every generator kind.