当前位置:   article > 正文

Linux 网络:网卡 promiscuous 模式疑云

Linux 网络:网卡 promiscuous 模式疑云

1. 前言

限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。

2. 问题场景

调试 Marvell 88E6320 时,发现 eth0 出人意料的进入了 promiscuous(混杂) 模式:

[ 5384.145131] device eth0 entered promiscuous mode

    Marvell 88E6320eth0 对应 SoCcpsw 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)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    3. 问题定位和分析

    经过一番分析,最后发现,是由下列命令导致 eth0 进入 promiscuous(混杂) 模式:

    ip link set dev lan4 address 62:b6:e1:6e:14:f8

      这里有两个问题:

      1. 操作的 lan4,为什么影响到了 eth0?
      2. 不过是一条设置 MAC 的指令,怎么会导致进入 promiscuous(混杂)模式?
      • 1

      跟踪下指令 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 {
      		...
      	}
      	...
      }
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
      • 79
      • 80
      • 81
      • 82
      • 83
      • 84
      • 85
      • 86
      • 87
      • 88
      • 89
      • 90
      • 91
      • 92
      • 93
      • 94
      • 95
      • 96
      • 97
      • 98
      • 99
      • 100

      到此,真相浮出水面,原来,交换芯片 port3 (lan3)port4 (lan4),要将数据转发给 eth0。从前面的信息看到,lan3eth0 公用了 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

      声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/木道寻08/article/detail/771804
      推荐阅读
      相关标签
        

      闽ICP备14008679号