python 捕获网络流量并发送到远程计算机

polkgigr  于 2023-04-28  发布在  Python
关注(0)|答案(1)|浏览(131)

我试图用tcpdump从网络中的一台机器捕获网络流量,并通过网络将每个数据包发送到另一台设备。我不能将tcpdump捕获的数据包保存在文件中,因为我需要实时地将数据包作为流(一个接一个)进行处理。
我使用下面的Python脚本捕获数据包并通过socket发送到另一台机器:

HOST = "192.168.xx.xx"
PORT = 65432

command = ['sudo', 'tcpdump', '-c', '1000', '-i', 'br0', '--packet-buffered', '-w', '-']
process = subprocess.Popen(command, stdout=subprocess.PIPE)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (HOST, PORT)
sock.connect(server_address)

for ts, packet_data in dpkt.pcap.Reader(process.stdout):
    eth_packet = dpkt.ethernet.Ethernet(packet_data)
    packet_bytes = eth_packet.pack()
    eth_packet.time = ts
    sock.sendall(packet_bytes)

sock.close()

在接收部分,我使用以下代码来接收和处理数据包(例如将数据包写入pcap文件):

HOST = "192.168.xx.xx"
PORT = 65432
BUF = 4096

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (HOST, PORT)
sock.bind(server_address)
sock.listen(1)

print('Waiting for a connection...')
connection, sender_address = sock.accept()

while True:
    # Receive data from the socket
    data = connection.recv(BUF)
    if not data:
        break
    # Parse the received data as a packet with dpkt and write it to the pcap file
    packet = dpkt.ethernet.Ethernet(data)
    pcap_writer.writepkt(packet)

# Close the PcapWriter and the socket
pcap_file.close()
connection.close()

但问题是,在接收端,数据包没有被正确接收。在Wireshark中打开时,某些数据包丢失,某些数据包损坏。我通过在套接字上发送之前将捕获的数据包存储在一个文件中来测试这一点。该文件包含了所有的数据包,一切都很好,我不知道我做错了什么,使数据包丢失或损坏。
简而言之,我需要在一台机器上捕获数据包,通过网络将它们发送到另一个节点,并能够逐个解析数据包并处理它们。我不确定这是否是最好的做法。
任何帮助是高度赞赏。

djmepvbi

djmepvbi1#

您需要一种对数据进行分帧的方法,以便能够识别数据包边界,并只将完整的数据包传递给dpkt。ethernet.Ethernet.
一个例子是用更高级别的协议替换TCP连接--它只是一个字节流。在本例中,我们使用zeromq,这是一个基于消息的协议,具有类似套接字的API。代码看起来与您当前的示例非常相似,但是因为它是对消息而不是字节进行操作,所以我们不需要担心自己的框架问题。

发送方

from contextlib import closing
import dpkt
import subprocess
import zmq

HOST = "localhost"
PORT = 65432
COUNT = 100

command = ['sudo', 'tcpdump', '-c', f'{COUNT}', '-i', 'enp12s0u1', '--packet-buffered', '-w', '-']

ctx = zmq.Context()
sock = ctx.socket(zmq.REQ)
sock.connect(f'tcp://{HOST}:{PORT}')

# We use the return from subprocess.Popen() as a context manager so that we
# wait() on the process after it exits.
with subprocess.Popen(command, stdout=subprocess.PIPE) as process, closing(sock):
    for (i, (ts, packet_data)) in enumerate(dpkt.pcap.Reader(process.stdout)):
        eth_packet = dpkt.ethernet.Ethernet(packet_data)
        packet_bytes = eth_packet.pack()
        print(f'sending packet {i}')
        sock.send(packet_bytes)

        # With a ZMQ REQ/REP pair, we need to call sock.recv() after sock.send() even
        # if we're discarding the data.
        sock.recv()

    # Send an empty message to indicate we've finished sending packets.
    sock.send(b'')
    ack = sock.recv()

接收方

from contextlib import closing
import dpkt
import zmq

HOST = "127.0.0.1"
PORT = 65432

ctx = zmq.Context()
sock = ctx.socket(zmq.REP)
sock.bind(f"tcp://{HOST}:{PORT}")

with open("packets.pcap", "wb") as fd, closing(sock):
    pcap_writer = dpkt.pcap.Writer(fd, nano=True)
    pkt_count = 0

    while True:
        data = sock.recv()

        # With a ZMQ REQ/REP pair we must call sock.send() after sock.recv().
        sock.send(b"")

        # Exit the loop when we receive an empty message.
        if not data:
            break

        packet = dpkt.ethernet.Ethernet(data)
        pcap_writer.writepkt(packet)

相关问题