文章目录
- 1. 前言
- 2. 问题场景
- 3. 问题定位和分析
- 4. 参考资料
1. 前言
限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。
2. 问题场景
调试 Marvell 88E6320
时,发现 eth0
出人意料的进入了 promiscuous(混杂)
模式:
[ 5384.145131] device eth0 entered promiscuous mode
Marvell 88E6320
和 eth0
对应 SoC
的 cpsw
MAC 芯片的连接拓扑结构如下:
系统网络设备配置如下:
# ifconfig
eth0 Link encap:Ethernet HWaddr 60:B6:E1:6E:14:F8
inet6 addr: fe80::62b6:e1ff:fe6e:14f8/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:247 errors:0 dropped:0 overruns:0 frame:0
TX packets:32 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:19440 (18.9 KiB) TX bytes:2560 (2.5 KiB)
Interrupt:47
lan3 Link encap:Ethernet HWaddr 60:B6:E1:6E:14:F8
inet addr:192.168.3.5 Bcast:0.0.0.0 Mask:255.255.255.0
inet6 addr: fe80::62b6:e1ff:fe6e:14f8/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:247 errors:0 dropped:0 overruns:0 frame:0
TX packets:16 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:13018 (12.7 KiB) TX bytes:1216 (1.1 KiB)
lan4 Link encap:Ethernet HWaddr 62:B6:E1:6E:14:F8
inet addr:192.168.3.8 Bcast:0.0.0.0 Mask:255.255.255.0
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
3. 问题定位和分析
经过一番分析,最后发现,是由下列命令导致 eth0
进入 promiscuous(混杂)
模式:
ip link set dev lan4 address 62:b6:e1:6e:14:f8
这里有两个问题:
1. 操作的 lan4,为什么影响到了 eth0?
2. 不过是一条设置 MAC 的指令,怎么会导致进入 promiscuous(混杂)模式?
跟踪下指令 ip link set dev lan4 address 62:b6:e1:6e:14:f8
流程:
/* ip link 程序通过 netlink 来进行 MAC 设置 */
sys_sendmsg()
...
netlink_unicast()
rtnetlink_rcv()
netlink_rcv_skb()
rtnetlink_rcv_msg()
rtnl_newlink()
do_setlink()
dev_set_mac_address()
// 触发 lan4 的 MAC 设置接口
ops->ndo_set_mac_address(dev, sa) = dsa_slave_set_mac_address()
// 接上面流程
static int dsa_slave_set_mac_address(struct net_device *dev, void *a)
{
// 这里回答了问题 1. 操作的 lan4,为什么影响到了 eth0?
// @dev : lan4
// @master: eth0
// 对 lan4 的操作反映到了 eth0
struct net_device *master = dsa_slave_to_master(dev);
...
if (!ether_addr_equal(addr->sa_data, master->dev_addr)) {
err = dev_uc_add(master, addr->sa_data);
...
}
...
}
int dev_uc_add(struct net_device *dev, const unsigned char *addr)
{
...
// 增加一条 单播过滤(unicast filtering)地址 到 eth0
err = __hw_addr_add(&dev->uc, addr, dev->addr_len,
NETDEV_HW_ADDR_T_UNICAST);
if (!err)
__dev_set_rx_mode(dev);
...
}
void __dev_set_rx_mode(struct net_device *dev)
{
...
if (!(dev->priv_flags & IFF_UNICAST_FLT)) { // 如果 eth0 不支持单播地址过滤
// 如果 eth0 不支持单播地址过滤,通过将 eth0 设置为 promiscuous(混杂)
// 变相的来支持 eth0 单播地址过滤。
if (!netdev_uc_empty(dev) && !dev->uc_promisc) { // 场景下,触发这条执行路径
__dev_set_promiscuity(dev, 1, false);
dev->uc_promisc = true;
} else if (netdev_uc_empty(dev) && dev->uc_promisc) {
__dev_set_promiscuity(dev, -1, false);
dev->uc_promisc = false;
}
}
// eth0 的 RX 模式 配置
if (ops->ndo_set_rx_mode)
ops->ndo_set_rx_mode(dev); // cpsw_ndo_set_rx_mode()
}
// 设置网卡 eth0 promiscuous(混杂)模式标记 IFF_PROMISC
static int __dev_set_promiscuity(struct net_device *dev, int inc, bool notify)
{
...
// 这里回答了问题 2. 不过是一条设置 MAC 的指令,怎么会导致进入 promiscuous(混杂)模式?
dev->flags |= IFF_PROMISC;
dev->promiscuity += inc;
...
if (dev->flags != old_flags) {
// 对应内核日志:
// [ 5384.145131] device eth0 entered promiscuous mode
pr_info("device %s %s promiscuous mode\n",
dev->name,
dev->flags & IFF_PROMISC ? "entered" : "left");
...
dev_change_rx_flags(dev, IFF_PROMISC);
}
if (notify)
__dev_notify_flags(dev, old_flags, IFF_PROMISC);
...
}
// eth0 的 rx 模式配置
static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
{
...
if (ndev->flags & IFF_PROMISC) {
/* Enable promiscuous mode */
cpsw_set_promiscious(ndev, true); // 将 eth0 设为 promiscuous(混杂)模式
...
} else {
...
}
...
}
到此,真相浮出水面,原来,交换芯片 port3 (lan3)
和 port4 (lan4)
,要将数据转发给 eth0
。从前面的信息看到,lan3
和 eth0
公用了 MAC ,lan4
配置了一个不同于 eth0
的 MAC,然后将 lan4
的 MAC 添加到 eth0
的单播过滤(unicast filtering)
MAC 列表,这样使得 eth0
除了可以接收 lan3
的数据外,也可以接收 lan4
的数据,同时由于 eth0
不支持 单播过滤(unicast filtering)
功能,所以只能将 eth0
配置为 promiscuous(混杂)模式
来变相的达到目的。
4. 参考资料
[1] 4.5.3.1. Unicast Frame Filtering
[2] Layerscape Software Development Kit User Guide
[3] UG10081: Layerscape Linux Distribution POC User Guide