Master Scapy: Build, Send, and Analyze Packets with Python
This guide introduces Scapy, a powerful interactive Python packet manipulation tool, covering installation, basic usage, packet creation, sending and receiving functions, layer inspection, packet export formats, sniffing, and advanced features such as sprintf and custom packet handlers, enabling network testing, analysis, and security tasks.
What is Scapy
Scapy is a powerful interactive packet manipulation program that can forge, decode, send, capture, match requests and responses for a wide range of protocols. It replaces tools such as hping, arpspoof, nmap, tcpdump, and can perform tasks like scanning, tracerouting, probing, unit testing, attacks, and network discovery.
Installation
Install directly with pip (Python 3):
<code>pip3 install scapy</code>Basic Usage
Enter the Scapy shell by typing
scapy. Use
ls()to list supported protocols and
lsc()to list functions.
ls()can also show protocol fields.
Sending and Receiving Packets
send
Sends packets at layer 3 (Scapy creates layer 2 headers) without receiving any response. Parameters:
loop– if non‑zero, packets are sent repeatedly until Ctrl‑C.
count– exact number of packets to send.
inter– interval in seconds between packets.
<code>>> send(IP(dst='8.8.8.8')/TCP(dport=53, flags='S'))
Sent 1 packets.
>>> send(IP(dst='8.8.8.8')/TCP(dport=53, flags='S'), count=10)
Sent 10 packets.
>>> send(IP(dst='8.8.8.8')/TCP(dport=53, flags='S'), loop=1)
Sent 1503 packets.</code>sendp
Sends at layer 2; you must provide a layer 2 header. Use the
ifaceargument to select the interface (defaults to
conf.iface).
<code>>> sendp(Ether()/IP(dst='1.2.3.4', ttl=(1,4)), iface='eth0')
Sent 4 packets.
>>> sendp('I’m travelling on Ethernet', iface='eth0', loop=1, inter=0.2)
Sent 11 packets.
>>> sendp(rdpcap('/tmp/pcapfile'))
Sent 11 packets.</code>sr
Sends packets and receives responses, returning a tuple of answered and unanswered lists.
<code>>> sr(IP(dst='60.205.177.168')/TCP(dport=[21,22,23]))
Begin emission:
Finished sending 3 packets.
Received 36 packets, got 2 answers, remaining 1 packets
(<Results: TCP:2 UDP:0 ICMP:0 Other:0>, <Unanswered: TCP:1>)</code>sr1
Sends packets and returns only the first response.
<code>>> p = sr1(IP(dst='www.baidu.com')/ICMP()/"asdqwe")
Received 2 packets, got 1 answer.</code>srloop
Continuously sends packets, receives responses, and displays them.
<code>>> packet = IP(dst='60.205.177.168')/ICMP()
>>> srloop(packet)
RECV 1: IP / ICMP 60.205.177.168 > 172.17.51.80 echo-reply 0
Sent 4 packets, received 4 packets. 100.0% hits.</code>Creating Packets
Scapy builds packets by stacking layers, mirroring the OSI model.
Basic packet can be created in one line:
<code>>> packet = Ether()/IP(dst='8.8.8.8')/TCP(dport=53, flags='S')</code>Or create each layer separately and combine with the '/' operator:
<code>>> l2 = Ether()
>>> l3 = IP(dst='8.8.8.8/30')
>>> l4 = TCP(dport=53, flags='S')
>>> packet = l2/l3/l4</code>Inspecting Packets
Use
ls(packet)to list field definitions and
packet.show()for a detailed view.
packet.show2()also assembles the packet and computes checksums and IHL.
<code>>> packet = IP()/TCP()
>>> ls(packet)
version : BitField = 4 (4)
... (snipped) ...
>>> packet.show()
###[ IP ]###
version= 4
ttl= 64
src= 127.0.0.1
dst= 127.0.0.1
###[ TCP ]###
sport= ftp_data
dport= http
flags= S
...
>>> packet.summary()
'IP / TCP 127.0.0.1:ftp_data > 127.0.0.1:http S'</code>Layer Interaction
Access fields directly (e.g.,
packet.dst) or via layer name (
packet[IP].dst). Check for a layer with
haslayer()or the
inoperator.
<code>>> if packet.haslayer(IP):
... print(packet[IP].dst)
8.8.8.8
>>> DNS in pkt
True</code>sprintf
The
sprintf()method fills format strings with packet field values, similar to C's sprintf.
<code>>> packet.sprintf("Ethernet source is %Ether.src% and IP proto is %IP.proto%")
'Ethernet source is 00:16:3e:0c:d1:ad and IP proto is tcp'
>>> a.sprintf("%dst% %IP.dst% vlan=%Dot1Q.vlan%")
'00:00:d4:ae:3f:71 192.168.0.1 vlan=42'</code>Packet Handlers
Define a lambda to process TCP packets:
<code>>> f = lambda x: x.sprintf("%IP.dst%:%TCP.dport%")
>>> f(IP(dst='8.8.8.8')/TCP())
'8.8.8.8:http'
>>> f(IP(dst='8.8.8.8')/UDP())
'8.8.8.8:??'</code>Conditional substrings in
sprintf()allow handling of different layers.
<code>>> f = lambda x: x.sprintf("=> {IP:ip=%IP.dst% {UDP:dport=%UDP.dport%}\n... {TCP:%TCP.dport%/%TCP.flags%}{ICMP:type=%ICMP.type%}} {!IP:not an IP packet}")
>>> f(IP()/TCP())
'=> ip=127.0.0.1 http/S'
>>> f(Ether()/ARP())
'=> not an IP packet'</code>Import and Export
PCAP format:
pkts = rdpcap('temp.cap')or
sniff(offline='temp.cap'). Export with
wrpcap('temp.cap', pkts).
Hexdump:
hexdump(s)displays packet bytes.
Hex string:
str(s)returns a hexadecimal representation.
Base64:
export_object(s)returns a base64‑encoded packet.
Sniffing
The
sniff()function captures traffic with options such as
count,
filter,
iface,
lfilter,
prn, and
timeout.
<code>>> sniff(count=4, iface='eth0')
<Sniffed: TCP:1 UDP:3 Other:0>
>>> pkts = sniff(count=1, filter="tcp and host 60.205.177.168 and port 80")
>>> pkts.summary()
Ether / IP / TCP 172.17.51.80:54578 > 60.205.177.168:http S
>>> pkts = sniff(offline='test.pcap')
>>> pkts.nsummary()
0000 Ether / IP / TCP 172.16.16.128:1606 > 74.125.95.104:http S
...</code>These capabilities make Scapy a versatile tool for network testing, security analysis, and protocol research.
Ops Development Stories
Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.