如何在Linux上正确地将网络接口置于混杂模式(How to properly put network interface into promiscuous mode on Linux)

那么你如何正确地做到这一点?

我知道如何通过创建套接字,然后使用ioctl设置IFF_PROMISC标志(如“ 如何检查C中的网络设备状态? ”和其他地方中所述),但这至少在理论上看起来有缺陷。

1)你通过ioctl读取标志2)你更新标志3)别人修改标志4)你通过ioctl设置更新的标志

有更好的方法还是我太担心?

后来我发现应该通过setsockopt(也没有竞赛)将接口添加到PACKET_MR_PROMISC,如下所示:

void set_promisc(const char *ifname, bool enable)
{
struct packet_mreq mreq = {0};
int sfd;
int action;
if ((sfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) {
perror("unable to open socket");
return;
}
mreq.mr_ifindex = if_nametoindex(ifname);
mreq.mr_type = PACKET_MR_PROMISC;
if (mreq.mr_ifindex == 0) {
perror("unable to get interface index");
return;
}
if (enable)
action = PACKET_ADD_MEMBERSHIP;
else
action = PACKET_DROP_MEMBERSHIP;
if (setsockopt(sfd, SOL_PACKET, action, &mreq, sizeof(mreq)) != 0) {
perror("unable to enter promiscouous mode");
return;
}
close(sfd);
}

不幸的是,这对接口没有任何影响,尽管它应该,如果我正确地解析文档 。 自2001年(tm)以来可能会被打破 ? pcap source中的评论也抱怨这一点。

So how do you do this properly?

I know how to do it by creating socket, then setting IFF_PROMISC flag using ioctl (as explained in "howto check a network devices status in C?" and elsewhere), but this looks flawed at least in theory.

1) you read flags via ioctl 2) you update flags 3) someone else modified flags 4) you set updated flags via ioctl

Is there a better way or do I simply worry too much?

Later I found that one should add interface to PACKET_MR_PROMISC via setsockopt (which also does not have a race) like this:

void set_promisc(const char *ifname, bool enable)
{
struct packet_mreq mreq = {0};
int sfd;
int action;
if ((sfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) {
perror("unable to open socket");
return;
}
mreq.mr_ifindex = if_nametoindex(ifname);
mreq.mr_type = PACKET_MR_PROMISC;
if (mreq.mr_ifindex == 0) {
perror("unable to get interface index");
return;
}
if (enable)
action = PACKET_ADD_MEMBERSHIP;
else
action = PACKET_DROP_MEMBERSHIP;
if (setsockopt(sfd, SOL_PACKET, action, &mreq, sizeof(mreq)) != 0) {
perror("unable to enter promiscouous mode");
return;
}
close(sfd);
}
Unfortunately this has no effect whatsoever on interface, although it should, if I unserstand the doc correctly. Possibly broken since 2001 (tm)? Comments in pcap source also complain about this.


最满意答案

PACKET_MR_PROMISC打开设备的混杂模式。 这不会反映在ifconfig显示的状态中,因为它不会修改设备上的全局IFF_PROMISC标志的状态。 这并不意味着它没有完成。 这就是pcap库现在的工作方式以及wireshark(以及其他十几个实用程序)可以打开设备并查看未发送到本地系统的数据包这一事实表明它可以工作。

每个设备上都有一个内部计数器,每次进程使用PACKET_MR_PROMISC时都会PACKET_MR_PROMISC ,当该进程消失时递减。 这解决了你最初描述的比赛。

从您提供的最后一个链接:

> IFF_PROMISC is not set,
It's not supposed to be set.
The correct way to put into promiscuous mode the device to which a
PF_PACKET socket is to do a SOL_PACKET/PACKET_ADD_MEMBERSHIP
"setsockopt()" call with PACKET_MR_PROMISC as the argument (see the
"packet(7)" man page), and that's what libpcap is doing.
The old way of directly setting IFF_PROMISC had problems - to quote the
comment at the front of "pcap-linux.c":
[snipped]
PACKET_MR_PROMISC turns on promiscuous mode for the device. That will not be reflected in the status shown by ifconfig as it does not modify the state of the global IFF_PROMISC flag on the device. That does not mean it hasn't been done though. This is how the pcap library works now and the fact that wireshark (and a dozen other utilities) can open a device and see packets not addressed to the local system shows that it works.
There is an internal counter on each device that is incremented each time a process uses PACKET_MR_PROMISC, and decremented when that process goes away. That solves the race you originally described.
From the last link you provided:
> IFF_PROMISC is not set,
It's not supposed to be set.
The correct way to put into promiscuous mode the device to which a
PF_PACKET socket is to do a SOL_PACKET/PACKET_ADD_MEMBERSHIP
"setsockopt()" call with PACKET_MR_PROMISC as the argument (see the
"packet(7)" man page), and that's what libpcap is doing.
The old way of directly setting IFF_PROMISC had problems - to quote the
comment at the front of "pcap-linux.c":
[snipped]
2017-01-16

几个月前我在番茄上做了这个。 你至少需要番茄v1.24,其中包括ipt_ROUTE。 它用于启用-j ROUTE和--tee功能。 / sbin / modprobe ipt_ROUTE 然后如上所述添加iptables规则。

I did this a few months back on Tomato. You need at least Tomato v1.24, which includes ipt_ROUTE. It is used to enable the -j ROUTE and -
...
你可以随时使用getifaddrs(3)这里是一个例子: #include 
#include 
#include 
#include 
int main(void) {
struct ifaddrs *ifap,*ifa;
getifaddrs(&ifap);
for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
...

要从软件访问iproute2工具提供的功能,您可以使用Linux rtnetlink API 。 为了让你更容易,使用像libnl这样的库。 如果遇到麻烦,已经有一个名为netlink的标签

To access the functionality provided by the iproute2 tools from software, you can use the Linux rtnetlink API. To make it easier for you, use libraries lik
...

PACKET_MR_PROMISC打开设备的混杂模式。 这不会反映在ifconfig显示的状态中,因为它不会修改设备上的全局IFF_PROMISC标志的状态。 这并不意味着它没有完成。 这就是pcap库现在的工作方式以及wireshark(以及其他十几个实用程序)可以打开设备并查看未发送到本地系统的数据包这一事实表明它可以工作。 每个设备上都有一个内部计数器,每次进程使用PACKET_MR_PROMISC时都会PACKET_MR_PROMISC ,当该进程消失时递减。 这解决了你最初描述的比赛。

...

与Rob Jones的建议一起,尝试使用像Wireshark这样的工具来确保您接收到您在接口上所期望的数据包。 至少这将确认(或否认)您的代码有问题。 还需要确保接口本身设置为混杂模式。 如果没有,那么你可以使用ioctl()来设置它:

ifr.ifr_flags |= IFF_PROMISC;
if( ioctl(sock, SIOCSIFFLAGS, &ifr) != 0 )
{
// handle error here
}

在应用程序运行时,请确保ifconfig报告该接口的PRO

...

这是因为您使用的SOL_PACKET仅用于packet(7)套接字。 对于原始INET套接字,您不需要该选项。 It's because you use SOL_PACKET which is only used for packet(7) sockets. For raw INET sockets, you don't need that option.

首先,在这种情况下检查您的任何调用是否失败,socket,bind,setsockopt。 使用perror()函数打印错误消息将帮助您诊断问题。 但是,对于接收多播数据报,您可能需要在使用IP_ADD_MEMBERSHIP套接字选项加入多播组时指定接口的IP地址。

setsockopt (sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
mreq结构的位置 struct ip_mreq
{
struct in_
...

您可以尝试使用Netlink的RTNETLINK( 1,2 )。 有关示例 ,请参阅使用RTNETLINK操作网络环境 。 You might try to use RTNETLINK from Netlink(1,2). See Manipulating the Networking Environment Using RTNETLINK for an exemple.