scapy でパケットをキャプチャできます。
※ここでは実際に設定、動作したものを掲載していますが、内容について保証するものではありません。流用される場合は各自の責任でお願いします。
- パケットのキャプチャは同期と非同期で行えます。同期は sniff 関数で、非同期は AsyncSniffer で実施します。ここでの同期、非同期という言葉はキャプチャ実施中にコンソールの制御を返すか否かという意味で使用しています。同期(sniff)でのキャプチャ中はコンソールのプロンプトが返らず、別のコマンドの入力ができません。非同期(AsyncSniffer)では開始直後にコンソールのプロンプトが返るため、キャプチャと並行して別のコマンドの入力が可能です。
- sniff および AsyncSniffer でキャプチャしたパケットは PCAP ファイルへ出力することが可能です。また PCAP ファイルからパケットを読み込むことが可能です。
- キャプチャしたパケットに対する操作を指定できます。例えばキャプチャしたパケットから必要な情報のみ表示することが可能です。
scapy の他の機能については「scapy について」からご参照ください。
参考:sniff のヘルプ
>>> help(sniff)
Help on function sniff in module scapy.sendrecv:
sniff(*args, **kwargs)
Sniff packets and return a list of packets.
Args:
count: number of packets to capture. 0 means infinity.
store: whether to store sniffed packets or discard them
prn: function to apply to each packet. If something is returned, it
is displayed.
--Ex: prn = lambda x: x.summary()
session: a session = a flow decoder used to handle stream of packets.
--Ex: session=TCPSession
See below for more details.
filter: BPF filter to apply.
lfilter: Python function applied to each packet to determine if
further action may be done.
--Ex: lfilter = lambda x: x.haslayer(Padding)
offline: PCAP file (or list of PCAP files) to read packets from,
instead of sniffing them
quiet: when set to True, the process stderr is discarded
(default: False).
timeout: stop sniffing after a given time (default: None).
L2socket: use the provided L2socket (default: use conf.L2listen).
opened_socket: provide an object (or a list of objects) ready to use
.recv() on.
stop_filter: Python function applied to each packet to determine if
we have to stop the capture after this packet.
--Ex: stop_filter = lambda x: x.haslayer(TCP)
iface: interface or list of interfaces (default: None for sniffing
on the default interface).
monitor: use monitor mode. May not be available on all OS
started_callback: called as soon as the sniffer starts sniffing
(default: None).
The iface, offline and opened_socket parameters can be either an
element, a list of elements, or a dict object mapping an element to a
label (see examples below).
For more information about the session argument, see
https://scapy.rtfd.io/en/latest/usage.html#advanced-sniffing-sniffing-sessions
Examples: synchronous
>>> sniff(filter="arp")
>>> sniff(filter="tcp",
... session=IPSession, # defragment on-the-flow
... prn=lambda x: x.summary())
>>> sniff(lfilter=lambda pkt: ARP in pkt)
>>> sniff(iface="eth0", prn=Packet.summary)
>>> sniff(iface=["eth0", "mon0"],
... prn=lambda pkt: "%s: %s" % (pkt.sniffed_on,
... pkt.summary()))
>>> sniff(iface={"eth0": "Ethernet", "mon0": "Wifi"},
... prn=lambda pkt: "%s: %s" % (pkt.sniffed_on,
... pkt.summary()))
Examples: asynchronous
>>> t = AsyncSniffer(iface="enp0s3")
>>> t.start()
>>> time.sleep(1)
>>> print("nice weather today")
>>> t.stop()
(END)
参考:AsyncSniffer のヘルプ
>>> help(AsyncSniffer)
Help on class AsyncSniffer in module scapy.sendrecv:
class AsyncSniffer(builtins.object)
| AsyncSniffer(*args, **kwargs)
|
| Sniff packets and return a list of packets.
|
| Args:
| count: number of packets to capture. 0 means infinity.
| store: whether to store sniffed packets or discard them
| prn: function to apply to each packet. If something is returned, it
| is displayed.
| --Ex: prn = lambda x: x.summary()
| session: a session = a flow decoder used to handle stream of packets.
| --Ex: session=TCPSession
| See below for more details.
| filter: BPF filter to apply.
| lfilter: Python function applied to each packet to determine if
| further action may be done.
| --Ex: lfilter = lambda x: x.haslayer(Padding)
| offline: PCAP file (or list of PCAP files) to read packets from,
| instead of sniffing them
| quiet: when set to True, the process stderr is discarded
| (default: False).
| timeout: stop sniffing after a given time (default: None).
| L2socket: use the provided L2socket (default: use conf.L2listen).
| opened_socket: provide an object (or a list of objects) ready to use
| .recv() on.
| stop_filter: Python function applied to each packet to determine if
| we have to stop the capture after this packet.
| --Ex: stop_filter = lambda x: x.haslayer(TCP)
| iface: interface or list of interfaces (default: None for sniffing
| on the default interface).
| monitor: use monitor mode. May not be available on all OS
| started_callback: called as soon as the sniffer starts sniffing
| (default: None).
|
| The iface, offline and opened_socket parameters can be either an
| element, a list of elements, or a dict object mapping an element to a
| label (see examples below).
|
| For more information about the session argument, see
| https://scapy.rtfd.io/en/latest/usage.html#advanced-sniffing-sniffing-sessions
|
| Examples: synchronous
| >>> sniff(filter="arp")
| >>> sniff(filter="tcp",
| ... session=IPSession, # defragment on-the-flow
| ... prn=lambda x: x.summary())
| >>> sniff(lfilter=lambda pkt: ARP in pkt)
| >>> sniff(iface="eth0", prn=Packet.summary)
| >>> sniff(iface=["eth0", "mon0"],
| ... prn=lambda pkt: "%s: %s" % (pkt.sniffed_on,
| ... pkt.summary()))
| >>> sniff(iface={"eth0": "Ethernet", "mon0": "Wifi"},
| ... prn=lambda pkt: "%s: %s" % (pkt.sniffed_on,
| ... pkt.summary()))
|
| Examples: asynchronous
| >>> t = AsyncSniffer(iface="enp0s3")
| >>> t.start()
| >>> time.sleep(1)
| >>> print("nice weather today")
| >>> t.stop()
|
| Methods defined here:
|
| __init__(self, *args, **kwargs)
| Initialize self. See help(type(self)) for accurate signature.
|
| join(self, *args, **kwargs)
|
| start(self)
| Starts AsyncSniffer in async mode
|
| stop(self, join=True)
| Stops AsyncSniffer if not in async mode
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables
|
| __weakref__
| list of weak references to the object
(END)
sniff でキャプチャ
sniff を実行すると直ちにキャプチャが開始されます。終了は、timeout オプションで指定した時間の経過や、count オプションで指定した件数分のキャプチャ終了等の条件が成立した場合、もしくは Ctrl+C 等で中断した場合となります。timeout と count の両方を指定した場合、どちらか一方の条件が成立した時点で sniff は終了します。
下記例は常時流れているパケットをキャプチャした結果です。iface オプションにはキャプチャ対象のインターフェイスを指定可能です。
>>> cap = sniff(iface="enp0s3", timeout=10, count=5)
>>> len(cap)
1
>>> cap.nsummary()
0000 Ether / IP / TCP 10.0.2.2:50068 > 10.0.2.44:ssh A / Padding
>>>
iface の指定が無ければ、conf.iface に定義されたインターフェイスが使用されます。
>>> conf.iface
<NetworkInterface enp0s3 [UP+BROADCAST+RUNNING+MULTICAST+LOWER_UP]>
>>>
フィルタを指定してキャプチャ
filter オプションでキャプチャ時のフィルタを指定できます。
>>> # プロトコル(icmp)でフィルタ
>>> cap = sniff(filter="icmp", count=2)
>>> cap.nsummary()
0000 Ether / IP / ICMP 10.0.2.44 > 10.0.2.40 echo-request 0 / Raw
0001 Ether / IP / ICMP 10.0.2.40 > 10.0.2.44 echo-reply 0 / Raw
>>>
>>> # ポート番号(53:dns)でフィルタ
>>> cap = sniff(filter="port 53", count=2)
>>> cap.nsummary()
0000 Ether / IP / UDP / DNS Qry b'www.test.co.jp.'
0001 Ether / IP / UDP / DNS Ans 1.2.3.4
>>>
>>> # プロトコル(icmp)と送信元アドレスでフィルタ
>>> cap = sniff(filter="icmp and src host 10.0.2.44", count=2)
>>> cap.nsummary()
0000 Ether / IP / ICMP 10.0.2.44 > 10.0.2.40 echo-request 0 / Raw
0001 Ether / IP / ICMP 10.0.2.44 > 10.0.2.40 echo-request 0 / Raw
>>>
>>> # プロトコル(icmp)と宛先アドレスでフィルタ
>>> cap = sniff(filter="icmp and dst host 10.0.2.40", count=2)
>>> cap.nsummary()
0000 Ether / IP / ICMP 10.0.2.44 > 10.0.2.40 echo-request 0 / Raw
0001 Ether / IP / ICMP 10.0.2.44 > 10.0.2.40 echo-request 0 / Raw
>>>
>>> # プロトコル(icmp)とサブネットでフィルタ
>>> cap = sniff(filter="icmp and net 10.0.2.0/24", count=2)
>>> cap.nsummary()
0000 Ether / IP / ICMP 10.0.2.44 > 10.0.2.40 echo-request 0 / Raw
0001 Ether / IP / ICMP 10.0.2.40 > 10.0.2.44 echo-reply 0 / Raw
>>>
PCAP ファイルへの出力
sniff でキャプチャしたパケットを PCAP ファイルに出力します。
>>> # キャプチャ
>>> cap = sniff(filter="icmp", count=2)
>>> cap.nsummary()
0000 Ether / IP / ICMP 10.0.2.44 > 10.0.2.40 echo-request 0 / Raw
0001 Ether / IP / ICMP 10.0.2.40 > 10.0.2.44 echo-reply 0 / Raw
>>>
>>> # PCAP へ出力
>>> wrpcap("/tmp/test.pcap", cap)
>>>
出力した PCAP ファイルは tshark 等で参照できます。
[root@rocky8-client scapy]# tshark -n -t a -r /tmp/test.pcap
Running as user "root" and group "root". This could be dangerous.
1 13:39:10.907014 10.0.2.44 → 10.0.2.40 ICMP 98 Echo (ping) request id=0x0001, seq=1/256, ttl=64
2 13:39:10.907948 10.0.2.40 → 10.0.2.44 ICMP 98 Echo (ping) reply id=0x0001, seq=1/256, ttl=64 (request in 1)
[root@rocky8-client scapy]#
PCAP ファイルからの読み込み
PCAP ファイルを sniff で読み込みます。
>>> cap2 = sniff(offline="/tmp/test.pcap")
>>> cap2.nsummary()
0000 Ether / IP / ICMP 10.0.2.44 > 10.0.2.40 echo-request 0 / Raw
0001 Ether / IP / ICMP 10.0.2.40 > 10.0.2.44 echo-reply 0 / Raw
>>>
キャプチャパケットに対するリアクション
prn オプションで、キャプチャした各パケットを制御するための処理を指定できます。
以下の例では、dig 実行時にキャプチャしたパケットを prn に指定した命令に従い編集、表示しています。
>>> # lambda の x には、キャプチャしたパケットが順次引き渡される
>>> sniff(filter="dst port 53", count=1, prn=lambda x : x[DNS].qd[0].sprintf("qn:%qname%, qt:%qtype%"))
qn:b'www.test.co.jp.', qt:A
<Sniffed: TCP:0 UDP:1 ICMP:0 Other:0>
>>>
prn にもう少し複雑な命令を指定してみます。
>>> custom_pr2 = lambda x : (x[DNS].sprintf("qr:%qr%, opc:%opcode%, rc:%rcode%, [qd:%qdcount% an:%ancount% ns:%nscount% ar:%arcount%], ") + x[DNS].qd[0].sprintf("qn:%qname%, qt:%qtype%"))
>>> sniff(filter="port 53", count=2, store=False, prn=custom_pr2)
qr:0, opc:QUERY, rc:ok, [qd:1 an:0 ns:0 ar:1], qn:b'www.test.co.jp.', qt:A
qr:1, opc:QUERY, rc:ok, [qd:1 an:1 ns:2 ar:3], qn:b'www.test.co.jp.', qt:A
<Sniffed: TCP:0 UDP:0 ICMP:0 Other:0>
>>>
同じ内容を、少し命令を変えて表示します。
>>> custom_pr3 = lambda x : x.sprintf("qr:%d, opc:%s, rc:%s, [qd:%d an:%d ns:%d ar:%d], qn:%s, qt:%d" %
... (x[DNS].qr, x[DNS].opcode, x[DNS].rcode, x[DNS].qdcount, x[DNS].ancount, x[DNS].nscount, x[DNS].arcount, x[DNS].qd[0].qname.decode('utf-8'), x[DNS].qd[0].qtype))
>>> sniff(filter="port 53", count=2, store=False, prn=custom_pr3)
qr:0, opc:0, rc:0, [qd:1 an:0 ns:0 ar:1], qn:www.test.co.jp., qt:1
qr:1, opc:0, rc:0, [qd:1 an:1 ns:2 ar:3], qn:www.test.co.jp., qt:1
<Sniffed: TCP:0 UDP:0 ICMP:0 Other:0>
>>>
sniff から返された値を変数で受け取らない場合でも、値は一時的にキャッシュされているため、直後にアンダースコア(_)で結果を拾うことができます。
sniff から返された値については捨ててもかまわない(パケットの処理は prn オプションの指定で行う)という場合は、store=False を指定することで値はキャッシュされなくなります。
>>> # store=False 指定なし
>>> sniff(filter="dst port 53", count=1, prn=lambda x : x[DNS].qd[0].sprintf("qn:%qname%, qt:%qtype%"))
qn:b'www.test.co.jp.', qt:A
<Sniffed: TCP:0 UDP:1 ICMP:0 Other:0>
>>> cap = _
>>> cap.summary()
Ether / IP / UDP / DNS Qry b'www.test.co.jp.'
>>>
>>> # store=False 指定あり
>>> sniff(filter="dst port 53", count=1, store=False, prn=lambda x : x[DNS].qd[0].sprintf("qn:%qname%, qt:%qtype%"))
qn:b'www.test.co.jp.', qt:A
<Sniffed: TCP:0 UDP:0 ICMP:0 Other:0>
>>> cap = _
>>> cap.summary()
>>>
キャプチャに使用したインターフェイスを確認
出力に sniffed_on を指定すると、どのインターフェイスで送受信したかわかります。
以下の例は、10.0.2.40 と 127.0.0.1 に対して ping を実行した際のキャプチャです。
>>> sniff(iface=["enp0s3","lo"], filter="icmp", prn=lambda x: x.sniffed_on + ": "+x.summary())
enp0s3: Ether / IP / ICMP 10.0.2.44 > 10.0.2.40 echo-request 0 / Raw
enp0s3: Ether / IP / ICMP 10.0.2.40 > 10.0.2.44 echo-reply 0 / Raw
lo: Ether / IP / ICMP 127.0.0.1 > 127.0.0.1 echo-request 0 / Raw
lo: Ether / IP / ICMP 127.0.0.1 > 127.0.0.1 echo-request 0 / Raw
lo: Ether / IP / ICMP 127.0.0.1 > 127.0.0.1 echo-reply 0 / Raw
lo: Ether / IP / ICMP 127.0.0.1 > 127.0.0.1 echo-reply 0 / Raw
^C<Sniffed: TCP:0 UDP:0 ICMP:6 Other:0>
>>>
AsyncSniffer でキャプチャ
AsyncSniffer では開始直後にコンソールの制御を返し、バックグラウンドでパケットキャプチャを継続します。sniff 同様、timeout オプションや count オプションを指定可能で、経過時間やキャプチャ件数が指定値に達すると処理を終了します。それらのオプションを指定しない場合、手動でキャプチャを停止する必要があります。
基本的な例として、count オプションを指定した場合と、オプションを指定しない(手動停止の)場合のログを以下に示します。
・count オプションを指定した場合
キャプチャの開始は start() で行います。開始すると直ちにプロンプトが返され、裏でキャプチャが継続されます。
join() を実行するとキャプチャの終了待ちになるようです。(*1)キャプチャが終了していれば直ちにプロンプトが返されますが、終了していなければ終了するまでプロンプトは返ってきません。
results で結果を受け取ることができます。
*1 join() についてざっくり調べましたが詳しい仕様はわかりませんでした。
>>> asniff = AsyncSniffer(filter="icmp", count=3)
>>> asniff.start()
>>> asniff.join()
>>> cap = asniff.results
>>> len(cap); cap.summary()
3
Ether / IP / ICMP 10.0.2.44 > 10.0.2.40 echo-request 0 / Raw
Ether / IP / ICMP 10.0.2.40 > 10.0.2.44 echo-reply 0 / Raw
Ether / IP / ICMP 10.0.2.44 > 10.0.2.40 echo-request 0 / Raw
>>>
・オプションを指定しない(手動停止の)場合
キャプチャの開始は start() で行います。開始すると直ちにプロンプトが返され、裏でキャプチャが継続されます。
キャプチャの停止は stop() で行います。開始時に count や timeout 等の終了条件が指定されていないため、join() を実行してしまうと延々とキャプチャの終了を待ち続けることになります。
結果は stop() から返されますが、results でも受け取ることができます。
>>> asniff = AsyncSniffer(filter="icmp")
>>> asniff.start()
>>> cap1 = asniff.stop()
>>> cap2 = asniff.results
>>> len(cap1); cap1.summary()
8
Ether / IP / ICMP 10.0.2.44 > 10.0.2.40 echo-request 0 / Raw
Ether / IP / ICMP 10.0.2.40 > 10.0.2.44 echo-reply 0 / Raw
Ether / IP / ICMP 10.0.2.44 > 10.0.2.40 echo-request 0 / Raw
Ether / IP / ICMP 10.0.2.40 > 10.0.2.44 echo-reply 0 / Raw
Ether / IP / ICMP 10.0.2.44 > 10.0.2.40 echo-request 0 / Raw
Ether / IP / ICMP 10.0.2.40 > 10.0.2.44 echo-reply 0 / Raw
Ether / IP / ICMP 10.0.2.44 > 10.0.2.40 echo-request 0 / Raw
Ether / IP / ICMP 10.0.2.40 > 10.0.2.44 echo-reply 0 / Raw
>>> len(cap2); cap2.summary()
8
Ether / IP / ICMP 10.0.2.44 > 10.0.2.40 echo-request 0 / Raw
Ether / IP / ICMP 10.0.2.40 > 10.0.2.44 echo-reply 0 / Raw
Ether / IP / ICMP 10.0.2.44 > 10.0.2.40 echo-request 0 / Raw
Ether / IP / ICMP 10.0.2.40 > 10.0.2.44 echo-reply 0 / Raw
Ether / IP / ICMP 10.0.2.44 > 10.0.2.40 echo-request 0 / Raw
Ether / IP / ICMP 10.0.2.40 > 10.0.2.44 echo-reply 0 / Raw
Ether / IP / ICMP 10.0.2.44 > 10.0.2.40 echo-request 0 / Raw
Ether / IP / ICMP 10.0.2.40 > 10.0.2.44 echo-reply 0 / Raw
>>>
コマンドの実行タイミングやオプションにより結果が変わることがありますが、ややこしいのでいくつか具体的な実行例を示すことにします。
以下は、start() に timeout、count のオプションを指定せず、stop() には join=True(default)を指定し手動停止するという基本的なパターンです。
stop() から結果が返されますが、results からも受け取ることが可能です。
>>> asniff = AsyncSniffer(filter="icmp")
>>> asniff.start()
>>> cap1 = asniff.stop(join=True)
>>> cap2 = asniff.results
>>> type(cap1); cap1
<class 'scapy.plist.PacketList'>
<Sniffed: TCP:0 UDP:0 ICMP:12 Other:0>
>>> type(cap2); cap2
<class 'scapy.plist.PacketList'>
<Sniffed: TCP:0 UDP:0 ICMP:12 Other:0>
>>>
以下は、start() に timeout、count のオプションを指定せず、stop() には join=False を指定し手動停止しています。
stop() は結果を返しません。
>>> asniff = AsyncSniffer(filter="icmp")
>>> asniff.start()
>>> cap1 = asniff.stop(join=False)
>>> cap2 = asniff.results
>>> type(cap1); cap1
<class 'NoneType'>
>>> type(cap2); cap2
<class 'scapy.plist.PacketList'>
<Sniffed: TCP:0 UDP:0 ICMP:12 Other:0>
>>>
以下は、start() に timeout、count のオプションを指定せず、stop() にはオプションを指定せずに手動停止しています。
結果は stop() に join=True を指定した場合と同じです。
>>> asniff = AsyncSniffer(filter="icmp")
>>> asniff.start()
>>> cap1 = asniff.stop()
>>> cap2 = asniff.results
>>> type(cap1); cap1
<class 'scapy.plist.PacketList'>
<Sniffed: TCP:0 UDP:0 ICMP:10 Other:0>
>>> type(cap2); cap2
<class 'scapy.plist.PacketList'>
<Sniffed: TCP:0 UDP:0 ICMP:10 Other:0>
>>>
以下は、start() に timeout オプションを指定し、指定した時間が経過したのちに results で結果を受け取っています。
時間経過によりキャプチャが停止していることが分かっているため join() による終了待ちは不要です。また、キャプチャが停止している状態で stop() を実行するとエラーとなります。
>>> asniff = AsyncSniffer(filter="icmp", timeout=3)
>>> asniff.start()
>>> time.sleep(5)
>>> cap = asniff.results
>>> type(cap); cap
<class 'scapy.plist.PacketList'>
<Sniffed: TCP:0 UDP:0 ICMP:6 Other:0>
>>>
以下は、start() に timeout オプションを指定し、指定した時間内に join() を実行しています。(*1)
join() 実行後、timeout で指定した時間が経過するまで、datetime.datetime.now() が実行待ちになっています。
ちなみに、join() はキャプチャ停止を待って終了するため、join() 後に stop() は使えません。
*1 時間間隔の確認のために、datetime.datetime.now() を実行しています。また、それに先立ち import datetime を実行しています。
>>> asniff = AsyncSniffer(filter="icmp", timeout=20)
>>> asniff.start(); datetime.datetime.now()
datetime.datetime(2025, 8, 4, 20, 16, 10, 604467)
>>> asniff.join(); datetime.datetime.now()
datetime.datetime(2025, 8, 4, 20, 16, 30, 606870)
>>> cap = asniff.results
>>> type(cap); cap
<class 'scapy.plist.PacketList'>
<Sniffed: TCP:0 UDP:0 ICMP:40 Other:0>
>>>
以下は、start() に timeout オプションを指定し、指定した時間内に stop() を実行しています。(*1)
stop() 実行によりキャプチャは直ちに停止され、datetime.datetime.now() も直後に実行されています。
*1 時間間隔の確認のために、datetime.datetime.now() を実行しています。また、それに先立ち import datetime を実行しています。
>>> asniff = AsyncSniffer(filter="icmp", timeout=20)
>>> asniff.start(); datetime.datetime.now()
datetime.datetime(2025, 8, 4, 20, 18, 0, 1809)
>>> cap1 = asniff.stop(join=True); datetime.datetime.now()
datetime.datetime(2025, 8, 4, 20, 18, 6, 620545)
>>> cap2 = asniff.results
>>> type(cap1); cap1
<class 'scapy.plist.PacketList'>
<Sniffed: TCP:0 UDP:0 ICMP:14 Other:0>
>>> type(cap2); cap2
<class 'scapy.plist.PacketList'>
<Sniffed: TCP:0 UDP:0 ICMP:14 Other:0>
>>>
以下は、start() に timeout オプションを指定し、指定した時間が経過した後に stop() を実行しています。
キャプチャ停止後の stop() 実行のためエラーとなっていますが、results による結果の受け取りは可能です。
stop() から返されているように見える値は、ひとつ前の試験結果の残骸です。
>>> asniff = AsyncSniffer(filter="icmp", timeout=5)
>>> asniff.start()
>>> time.sleep(6)
>>> cap1 = asniff.stop(join=True)
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/usr/local/lib/python3.11/site-packages/scapy/sendrecv.py", line 1410, in stop
raise Scapy_Exception("Not running ! (check .running attr)")
scapy.error.Scapy_Exception: Not running ! (check .running attr)
>>> cap2 = asniff.results
>>> type(cap1); cap1
<class 'scapy.plist.PacketList'>
<Sniffed: TCP:0 UDP:0 ICMP:14 Other:0>
>>> type(cap2); cap2
<class 'scapy.plist.PacketList'>
<Sniffed: TCP:0 UDP:0 ICMP:9 Other:0>
>>>
sniff 同様 prn オプションを使用可能です。
>>> asniff = AsyncSniffer(filter="icmp", store=False, prn=lambda x: x[IP].sprintf("%src% >>> %dst%"))
>>> asniff.start(); time.sleep(5); cap = asniff.stop()
10.0.2.44 >>> 10.0.2.40
10.0.2.40 >>> 10.0.2.44
10.0.2.44 >>> 10.0.2.40
10.0.2.40 >>> 10.0.2.44
10.0.2.44 >>> 10.0.2.40
10.0.2.40 >>> 10.0.2.44
10.0.2.44 >>> 10.0.2.40
10.0.2.40 >>> 10.0.2.44
10.0.2.44 >>> 10.0.2.40
10.0.2.40 >>> 10.0.2.44
>>>