如何提高Scapy读取大文件的性能

5

我需要读取和解析过大无法加载至内存的 .pcap 文件。目前我正在使用离线模式下的嗅探(sniff)。

sniff(offline=file_in, prn=customAction, store=0)

使用一个自定义的函数customAction,大致如下:

customAction(packet):
    global COUNT
    COUNT = COUNT + 1
    # do some other stuff that takes practically 0 time

目前这个程序处理数据包的速度太慢了。我已经在一个“驱动”程序中使用子进程来同时在不同的核心上运行此脚本,但我真的需要提高单核性能。

我尝试使用pypy,但使用pypy的性能比使用python3(anaconda)仅提高不到10%,让我很失望。

使用pypy运行50k个数据包的平均时间为52.54秒

使用python3运行50k个数据包的平均时间为56.93秒

有没有办法加快速度?

编辑:下面是cProfile的结果,可以看到代码在进行分析时稍微慢了一点,但所有时间都花费在scapy上。

66054791 function calls (61851423 primitive calls) in 85.482 seconds

Ordered by: cumulative time

ncalls            tottime  percall  cumtime  percall filename:lineno(function)
957/1             0.017    0.000    85.483   85.483  {built-in method builtins.exec}
    1             0.001    0.001    85.483   85.483  parser-3.py:1(<module>)
    1             0.336    0.336    83.039   83.039  sendrecv.py:542(sniff)
50001             0.075    0.000    81.693    0.002  utils.py:817(recv)
50001             0.379    0.000    81.618    0.002  utils.py:794(read_packet)
795097/50003      3.937    0.000    80.140    0.002  base_classes.py:195(__call__)
397549/50003      6.467    0.000    79.543    0.002  packet.py:70(__init__)
397545/50000      1.475    0.000    76.451    0.002  packet.py:616(dissect)
397397/50000      0.817    0.000    74.002    0.001  packet.py:598(do_dissect_payload)
397545/200039     6.908    0.000    49.511    0.000  packet.py:580(do_dissect)
199083            0.806    0.000    32.319    0.000  dns.py:144(getfield)
104043            1.023    0.000    22.996    0.000  dns.py:127(decodeRR)
397548            0.343    0.000    15.059    0.000  packet.py:99(init_fields)
397549            6.043    0.000    14.716    0.000 packet.py:102(do_init_fields)
6673299/6311213   6.832    0.000    13.259    0.000  packet.py:215(__setattr__)
3099782/3095902   5.785    0.000    8.197    0.000  copy.py:137(deepcopy)
3746538/2335718   4.181    0.000    6.980    0.000  packet.py:199(setfieldval)
149866            1.885    0.000    6.678    0.000  packet.py:629(guess_payload_class)
738212            5.730    0.000    6.311    0.000  fields.py:675(getfield)
1756450           3.393    0.000    5.521    0.000  fields.py:78(getfield)
49775             0.200    0.000    5.401    0.000  dns.py:170(decodeRR)
1632614           2.275    0.000    4.591    0.000  packet.py:191(__getattr__)
985050/985037     1.720    0.000    4.229    0.000  {built-in method builtins.hasattr}
326681/194989     0.965    0.000    2.876    0.000  packet.py:122(add_payload)
...

编辑2:完整的代码示例:

from scapy.all import *
from scapy.utils import PcapReader
import time, sys, logging


COUNT    = 0
def customAction(packet):
global COUNT
COUNT = COUNT + 1

file_temp = sys.argv[1]
path      = '/'.join(file_temp.split('/')[:-2])
file_in   = '/'.join(file_temp.split('/')[-2:])
name      = file_temp.split('/')[-1:][0].split('.')[0]


os.chdir(path)
q_output_file = 'processed/q_' + name + '.csv'
a_output_file = 'processed/a_' + name + '.csv'
log_file      = 'log/' + name + '.log'

logging.basicConfig(filename=log_file, level=logging.DEBUG)

t0=time.time()
sniff(offline=file_in, prn=customAction, lfilter=lambda x:x.haslayer(DNS), store=0)
t1=time.time()

logging.info("File '{}' took {:.2f} seconds to parse {} packets.".format(name, t1-t0, COUNT))

根据 https://gist.github.com/dpifke/2244911 的说法,defaultdict 不应该是 pypy 比 python3 更慢的原因。通过排除法,显然罪魁祸首是 scapy + pypy。 - deltap
经过一些代码调整,我现在可以使用pypy获得10%的执行速度提升。 - deltap
1
你是否通过性能分析器运行过这段代码,以查看你实际上花费时间的地方? - MatsLindh
@MatsLindh 我使用了时间库来实现计时。所有的时间都花在调用sniff()上了。在sniff()内部,我不知道大部分时间花在哪里。sniff()的源代码似乎在这里:https://github.com/secdev/scapy/blob/81f10d9bb0e5360fb24366bb0813c5cf4c51c74c/scapy/sendrecv.py - deltap
你能提供一个具体的例子吗?比如,一个循环调用sniff()函数大约十秒钟,并使用相同的虚拟参数,其中sniff()函数可以从github包中安装。对我们来说,有一个可以运行的起点会很有用。 - Armin Rigo
显示剩余3条评论
2个回答

1

看起来scapy导致PyPy的JIT热身时间很长,但是如果你运行足够长的时间,JIT仍然会工作。这里是我得到的结果(在Linux 64上):

size of .pcap        CPython time        PyPy time
2MB                  4.9s                7.3s
5MB                  15.3s               9.1s
15MB                 1m15s               21s

这些文件中有多少个数据包?我的含有5万个数据包的文件大约有8MB大小,而且运行时间比你的显著长。 - deltap
我在日志中看到了接近十几个“数据包”的数字。这意味着您没有告诉我们所需的全部信息。请提供一个完整的示例;它可以是互联网上可用的一些公共.pcap文件。 - Armin Rigo

0

我认为简短的答案是Scapy太慢了。我尝试使用sniff()或PcapReader仅扫描pcap文件,而不对数据包进行任何操作。这个过程从我的SSD读取速度不到3MB/s,CPU使用率达到了100%。Python中还有其他的pcap阅读库。我建议尝试其中之一。


1
如果您能提及或建议具体的库,那将非常有帮助。 - charlesreid1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接