如何使用Twisted嗅探网络接口?

时间:2018-12-08 12:17:05

标签: python twisted scapy packet-sniffers

我需要在Twisted代码中从网络接口接收原始数据包。数据包将没有正确的IP或MAC地址,也没有有效的报头,因此我需要原始的东西。

我尝试研究twisted.pair,但无法弄清楚如何使用它来获取原始界面。

通常,我会使用scapy.all.sniff。但是,这很困难,因此我不能仅将它与Twisted一起使用。 (我也不能将scapy.all.sniff用于超时和繁忙循环,因为我不想丢失数据包。)

一个可能的解决方案是在线程中运行scapy.all.sniff,并在收到数据包时以某种方式回调Twisted。这似乎有点不雅(而且我也不知道该怎么做,因为我是一名Twisted初学者),但是如果我找不到更好的东西,我可能会解决。

1 个答案:

答案 0 :(得分:-1)

您可以运行分布式系统,并将数据通过中央排队系统传递。遵循Unix的哲学,创建一个可以完成一些任务并且很好完成的应用程序。创建一个嗅探数据包的应用程序(您可以在此处使用scapy,因为如果阻塞任何内容都没有关系),然后将它们发送到队列(RabitMQ,Redis,SQS等),然后让另一个应用程序处理队列中的数据包。这种方法应该让您头痛的次数最少。

如果您需要在单个应用程序中运行所有程序,则线程/多处理是唯一的选择。但是,您需要遵循一些设计模式。您还可以将以下代码分解为单独的功能,并使用专用的排队系统。

from threading import Thread
from time import sleep
from twisted.internet import defer, reactor

class Sniffer(Thread):
    def __init__(self, _reactor, shared_queue):
        super().__init__()
        self.reactor = _reactor
        self.shared_queue = shared_queue

    def run(self):
        """
        Sniffer logic here
        """
        while True:
            self.reactor.callFromThread(self.shared_queue.put, 'hello world')
            sleep(5)

@defer.inlineCallbacks
def consume_from_queue(_id, _reactor, shared_queue):
    item = yield shared_queue.get()
    print(str(_id), item)
    _reactor.callLater(0, consume_from_queue, _id, _reactor, shared_queue)

def main():
    shared_queue = defer.DeferredQueue()
    sniffer = Sniffer(reactor, shared_queue)
    sniffer.daemon = True
    sniffer.start()

    workers = 4
    for i in range(workers):
        consume_from_queue(i+1, reactor, shared_queue)

    reactor.run()

main()

Sniffer类在Twisted的控制范围之外开始。请注意sniffer.daemon = True,这是为了在主线程停止时停止线程。如果将其设置为False(默认值),则仅当所有线程都结束时,应用程序才会退出。根据手头的任务,这可能或不一定总是可能的。如果您可以从嗅探中休息一下以检查线程事件,那么您也许能够以更安全的方式停止线程。

self.reactor.callFromThread(self.shared_queue.put, 'hello world')是必需的,以便放入队列中的项目发生在主反应堆线程中,而不是Sniffer执行的线程中。这样做的主要好处是,将对来自线程的消息进行某种形式的同步(假设您计划扩展以嗅探多个接口)。另外,我不确定DeferredQueue对象是否是线程安全的:)我将它们视为不是线程安全的。

由于在这种情况下Twisted不管理线程,因此开发人员执行线程至关重要。注意worker循环和consume_from_queue(i+1, reactor, shared_queue)。该循环确保仅所需数量的工人在处理任务。在consume_from_queue()函数内部,shared_queue.get()将等待(无阻塞),直到将某个项目放入队列中,打印该项目,然后安排另一个consume_from_queue()