Scapy
What It Is
Scapy is a Python packet crafting, sending, sniffing, and decoding toolkit. It is useful for labs, protocol learning, validation, reproducing packet behavior, and controlled troubleshooting. It is also sharp enough to cause problems if you spray traffic at production networks without permission.
Use Scapy in a lab first. Be explicit about interface, destination, count, and rate.
Setup
python3 -m venv .venv
source .venv/bin/activate
python -m pip install scapy
python
Inside Python:
from scapy.all import *
Watch out: Sending and sniffing often require elevated privileges. On macOS and Linux, that usually means running the Python process with permissions that can open raw sockets or capture interfaces.
Basic Discovery
| Command | Purpose |
|---|---|
ls() |
List available protocol layers |
ls(IP) |
Show fields for a layer |
lsc() |
List Scapy command helpers |
conf |
Show or set Scapy configuration |
ifaces |
List interfaces known to Scapy |
hexdump(pkt) |
Show packet bytes |
pkt.show() |
Decode packet fields |
pkt.summary() |
One-line packet summary |
Packet Building
pkt = IP(dst="192.0.2.10") / ICMP()
pkt.show()
Layer 2 packet:
frame = Ether(dst="ff:ff:ff:ff:ff:ff") / IP(dst="192.0.2.10") / UDP(dport=53)
Set fields after creation:
pkt = IP(src="10.0.0.10", dst="10.0.0.20") / TCP(dport=443, flags="S")
pkt.ttl = 5
Ranges and random values:
IP(dst="192.0.2.0/30")
IP(ttl=(1, 30))
Ether(dst=RandMAC())
IP(id=RandShort())
Sending
| Function | Layer | Use |
|---|---|---|
send(pkt) |
Layer 3 | Send IP packet and let OS handle Layer 2 |
sendp(pkt) |
Layer 2 | Send full Ethernet frame |
sr(pkt) |
Layer 3 | Send and receive replies |
sr1(pkt) |
Layer 3 | Return first reply |
srp(pkt) |
Layer 2 | Send and receive Ethernet frames |
srloop(pkt) |
Layer 3 | Repeat send and receive |
Examples:
send(IP(dst="192.0.2.10") / ICMP(), count=3)
reply = sr1(IP(dst="192.0.2.10") / ICMP(), timeout=2)
ans, unans = sr(IP(dst="192.0.2.10") / TCP(dport=[22, 80, 443], flags="S"), timeout=2)
Watch out: Always set count, timeout, and iface when testing outside a disposable lab.
Sniffing
pkts = sniff(iface="en0", count=10, filter="tcp port 443")
pkts.summary()
Process packets as they arrive:
def seen(pkt):
print(pkt.summary())
sniff(iface="en0", filter="icmp", prn=seen, store=False, count=5)
Modern note: Scapy capture filters use libpcap syntax. Display-style filters from Wireshark are different.
Useful Patterns
Traceroute-style TTL test:
ans, unans = sr(IP(dst="192.0.2.10", ttl=(1, 8)) / ICMP(), timeout=2)
ans.summary()
TCP SYN check:
syn = IP(dst="192.0.2.10") / TCP(dport=443, flags="S")
reply = sr1(syn, timeout=2)
reply.summary() if reply else "no reply"
Read and write packet captures:
pkts = rdpcap("capture.pcap")
wrpcap("filtered.pcap", [p for p in pkts if TCP in p])
Troubleshooting
| Symptom | Check | Likely Cause |
|---|---|---|
| Nothing sends | Privileges, route, interface | Raw socket or interface issue |
| No replies | Firewall, wrong source, timeout, return path | Packet sent but response blocked |
| Sniff sees nothing | Interface, filter, permissions, offload | Capture setup issue |
| Packet malformed | Field types, checksums, layer order | Bad packet construction |
| Unexpected source IP | OS route selection | Layer 3 send uses routing table |
| Too much traffic | Count, loop, inter, rate | Missing limits |
Safety
- Use documentation prefixes like
192.0.2.0/24,198.51.100.0/24, and203.0.113.0/24in examples. - Get authorization before sending crafted packets on any network you do not own.
- Prefer a lab VLAN, isolated namespace, or packet capture file for learning.
- Avoid fuzzing production services.
- Rate-limit tests and keep packet counts explicit.
Watch Out
- Do not confuse Scapy's Python object model with packets already on the wire.
- Do not assume checksums and lengths are final until the packet is built.
- Do not run infinite send loops by accident.
- Do not use Wireshark display filters in Scapy sniff filters.
- Do not test spoofed traffic on production networks without approval.