为什么即使未启用混杂模式,我也可以捕获所有传入数据包?

为什么即使未启用混杂模式,我也可以捕获所有传入数据包?

我使用下面的命令来捕获接口上的旁路数据包ens160

tcpdump -i ens160 -w test.pcap

在此期间,我使用以下命令来检查是否启用了混杂模式ens160

ifconfig ens160 | grep -i promisc 
ip a show ens160 | grep -i promisc
netstat -i | grep ens160

这些命令的所有输出都指向混杂模式ens160,但我仍然可以在 上看到不属于 ens160 的数据包test.pcap

我知道tcpdump默认情况下适用于混杂模式,但在此期间混杂模式ens160被禁用。

为什么即使未启用混杂模式我也可以捕获所有传入数据包?

答案1

长话短说

在现代 Linux 上,接口使用计数器promiscuity来了解其操作状态何时应该是混杂的 ( > 0 ) 或不是混杂的 ( = 0 )。接口PROMISC属性标志只是将计数器加 1 的方法之一promiscuity

tcpdump没有更改接口的PROMISC标志,但确实请求在此接口上的原始数据包套接字上接收混杂流量,自动增加计数器promiscuity,从而将接口设置为混杂模式。

ifconfig,(在 Linux 上)已过时,无法显示计数器promiscuity。也不会netstat -i

改用:

ip -details link show dev ens160

或使用 JSON 和jq:

ip -d -j link show dev ens160 | jq '.[].promiscuity'

还可以在事件模式(但不使用 JSON)以及其他链接更改中跟踪它:

ip -d monitor link dev ens160

冗长的答案

在 Linux 上这是一个过时的命令,应该在所有地方用and/orifconfig替换它。ip linkip addr

在这里它确实很重要,因为可以设置的接口属性(PROMISC例如 obsoleteifconfig ens160 promisc或 with)ip link set ens160 promisc on不会被更改,tcpdump并且不是重要的属性。ifconfig无法显示仅适用于ip link:此处promiscuity计数器的新功能。

这个答案依赖于strace发现可能发生什么不同的情况。


使用ifconfig ens160 promisc依赖于ioctl(2)-based API 的 obsolete,在 Linux 上被取代,但仍然可用于向后兼容,strace显示:

...
ioctl(4, SIOCGIFFLAGS, {ifr_name="ens160", ifr_flags=IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_MULTICAST}) = 0
ioctl(4, SIOCSIFFLAGS, {ifr_name="ens160", ifr_flags=IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_PROMISC|IFF_MULTICAST}) = 0
...

ip link set dev ens160 promisc on与取代一起使用(rt)netlink(7)套接字 API(单行被分割以提高可读性(并且 ARPHRD_NETROM 可能是从strace值 0 开始的解码错误)):

...
sendmsg(3, {msg_name={sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000},
            msg_namelen=12,
            msg_iov=[{
                iov_base={
                    {len=32, type=RTM_NEWLINK, flags=NLM_F_REQUEST|NLM_F_ACK, seq=1684784897, pid=0},
                    {ifi_family=AF_UNSPEC, ifi_type=ARPHRD_NETROM,
                     ifi_index=if_nametoindex("ens160"),
                     ifi_flags=IFF_PROMISC, ifi_change=0x100}},
                iov_len=32}],
            msg_iovlen=1, msg_controllen=0, msg_flags=0}, 0) = 32
...

两者都使用两个不同的 API 来启用恩斯160的接口属性 IFF_PROMISC。

tcpdump本身默认使用混杂模式(为了下面的解释,我没有写“启用接口属性”而是“使用混杂模式”)。为了防止它这样做,应该使用-p选项:

-p
--no-promiscuous-mode

不要将接口置于混杂模式。请注意,接口可能由于某些其他原因而处于混杂模式;因此, -p不能用作 的缩写ether host {local-hw-addr} or ether broadcast

它不是通过将接口属性设置为混杂来实现这一点,而是通过请求在此接口上使用的数据包套接字上接收混杂流量:

...
socket(AF_PACKET, SOCK_RAW, htons(0 /* ETH_P_??? */)) = 4
...
bind(4, {
         sa_family=AF_PACKET,
         sll_protocol=htons(0 /* ETH_P_??? */),
         sll_ifindex=if_nametoindex("ens160"),
         sll_hatype=ARPHRD_NETROM,
         sll_pkttype=PACKET_HOST, sll_halen=0
     }, 20) = 0
getsockopt(4, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
setsockopt(4, SOL_PACKET, PACKET_ADD_MEMBERSHIP, {
               mr_ifindex=if_nametoindex("ens160"),
               mr_type=PACKET_MR_PROMISC,
               mr_alen=0, mr_address=ca:de:8a:4a:1c:38},
           }, 16) = 0
...

不同之处在于Linux的处理方式:它会统计混杂模式下每个资源的使用情况,只要有这些资源之一,接口操作状态就是混杂的。一旦计数器降至零,接口操作状态就不再是混杂的。仅当从 0 更改为 >0 或从 >0 更改为 0 时,才会发生重新配置本身(触发实际硬件 NIC 上的实际硬件重新配置)。

这样的资源可以用来做什么呢?

  • 显然设置接口属性PROMISC
  • 将接口设置为桥接端口
  • 就像以前一样tcpdump,请求接收接口上打开的原始数据包套接字上的所有/混杂流量。这是可堆叠使用。每个并发用户都会将计数器加 1。
  • 创建链接到该接口的 MACVLAN 接口
  • 其他情况?...

当接口启动时,这些用途中的每一个都会添加到操作计数器中。此计数器不适用于较旧的 ioctl API,而仅适用于进行新开发的较新的 netlink API。ifconfig不能显示,ip -details link可以显示。没有-details该属性未显示,因为很少需要它来执行任何有用的操作。


虚拟 veth 接口示例test0

# ip link add name test0 up type veth
# ip -details link show dev test0
14: test0@veth0: <NO-CARRIER,BROADCAST,MULTICAST,UP,M-DOWN> mtu 1500 qdisc noqueue state LOWERLAYERDOWN mode DEFAULT group default qlen 1000
    link/ether ca:de:8a:4a:1c:38 brd ff:ff:ff:ff:ff:ff promiscuity 0  allmulti 0 minmtu 68 maxmtu 65535 
    veth addrgenmode eui64 numtxqueues 4 numrxqueues 4 gso_max_size 65536 gso_max_segs 65535 tso_max_size 524280 tso_max_segs 65535 gro_max_size 65536 

它被视为:promiscuity 0.

或者使用 JSON 输出和jq:

# ip -details -json link show dev test0 | jq '.[].promiscuity'
0

现在:

# ip link set dev test0 promisc on
# ip -d -j link show dev test0 | jq '.[].promiscuity'
1
# ip link add name br0 type bridge
# ip link set dev test0 master br0
# ip -d -j link show dev test0 | jq '.[].promiscuity'
2
# tcpdump -n -i test0 src 192.0.2.2 and dst 198.51.100.2 &
[1] 128657
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on test0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
# ip -d -j link show dev test0 | jq '.[].promiscuity'
3
# tcpdump -n -i test0 src 192.0.2.2 and dst 198.51.100.2 &
[2] 128670
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on test0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
# ip -d -j link show dev test0 | jq '.[].promiscuity'
4

因此,现在有 4 个资源使用计数:2 个来自内核,2 个来自用户态原始数据包套接字。

只要接口promiscuity> 0,它就会被配置为混杂模式。与简单标志相比的优点是它提供资源核算,因此无需猜测是否应该在完成后将接口重置为非混杂,或者在有机会进行清理之前被中断(例如:)kill -9 %1,要求管理员考虑接口是否不应再处于混杂模式或应该保留(例如:它是桥接端口)。

相关内容