我使用下面的命令来捕获接口上的旁路数据包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 link
ip 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
,要求管理员考虑接口是否不应再处于混杂模式或应该保留(例如:它是桥接端口)。