Python套接字:在Linux中启用混杂模式

13 投票
2 回答
16149 浏览
提问于 2025-04-16 18:00

我们知道,Python可以通过以下方式在Windows系统下启用混杂模式:

s.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)

不过,RCVALL_*和SIO_*这些选项只在Windows上可用。 在Linux系统中,使用C语言的socket API时,可以用:

ethreq.ifr_flags |= IFF_PROMISC;
ioctl(sock, SIOCSIFFLAGS, &ethreq);

或者通过:

setsockopt(sock, SOL_PACKET, PACKET_ADD_MEMBERSHIP, PACKET_MR_PROMISC)

那么在Python的socket API中,有没有办法让我们在Linux上设置混杂模式呢?

2 个回答

5

我想到另一种方法。虽然可能没有那么优雅,但看起来效果不错。

在Linux系统中(需要管理员权限),可以使用:

# ifconfig eth0 promisc
# ifconfig eth0 -promisc

来开启或关闭你的网络接口的混杂模式(这里是eth0)。

所以,在Python中(同样需要管理员权限),可以使用:

import os
ret =  os.system("ifconfig eth0 promisc")
if ret == 0:
     <Do something>

欢迎大家对这种做法提出意见。

20

使用一个 AF_NETLINK 套接字来发出请求,开启 IFF_PROMISC 模式。在Linux上,Python可以创建 AF_NETLINK 套接字:

>>> from socket import AF_NETLINK, SOCK_DGRAM, socket 
>>> s = socket(AF_NETLINK, SOCK_DGRAM)
>>>

你可以查看 netlink(7) 手册页末尾的示例,了解如何发出 netlink 请求。你可以使用 ctypes(或者 struct)来构建要通过 netlink 套接字发送的序列化 nlmsghdr 消息。你可能还需要调用 sendmsgrecvmsg,因为 Python 目前还没有提供这些API。另外,还有一些 第三方模块 可以使用,这些模块提供了这两个API。

另外,你也可以选择传统的方法,使用 ioctl,这实际上会简单很多。

首先,使用 ctypes 定义 ifreq 结构:

import ctypes

class ifreq(ctypes.Structure):
    _fields_ = [("ifr_ifrn", ctypes.c_char * 16),
                ("ifr_flags", ctypes.c_short)]

然后创建一个套接字,用于 ioctl 调用:

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

接着,从 /usr/include 中复制几个常量值,因为这些在 Python 中并没有提供:

IFF_PROMISC = 0x100
SIOCGIFFLAGS = 0x8913
SIOCSIFFLAGS = 0x8914

创建一个 ifreq 结构的实例,并填充它以达到想要的效果:

ifr = ifreq()
ifr.ifr_ifrn = "eth4"

使用 ioctl 调用填充 ifr_flags 字段,这样就不会覆盖接口上已经设置的标志:

import fcntl

fcntl.ioctl(s.fileno(), SIOCGIFFLAGS, ifr) # G for Get

添加混杂模式标志:

ifr.ifr_flags |= IFF_PROMISC

并在接口上设置这些标志:

fcntl.ioctl(s.fileno(), SIOCSIFFLAGS, ifr) # S for Set

要移除这个标志,可以将其掩码掉,然后重新设置:

ifr.ifr_flags &= ~IFF_PROMISC
fcntl.ioctl(s.fileno(), SIOCSIFFLAGS, ifr)

撰写回答