Linux网卡混杂模式下收到mac地址不是自己的mac是否会上送网络层处理
https://segmentfault.com/a/1190000021291692
实验环境
注:在ubuntu18:04环境中,创建了一个网络命名空间mac,使用veth网卡与系统互联。peer为veth1,本端为eth0。系统中海油一个docker0网卡,具体参数如上图所示。
配置如下
sudo ip netns add mac sudo ip link add veth1 type veth peer name eth0 netns mac sudo ip link set veth1 up sudo ip addr add 10.10.10.1/24 dev veth1 sudo ip link set veth1 address 00:01:02:03:04:05 sudo ip netns exec mac ip link sudo ip netns exec mac ip link set lo up sudo ip netns exec mac ip link set eth0 up sudo ip netns exec mac ip addr add 10.10.10.2/24 dev eth0 sudo ip netns exec mac ip route add default via 10.10.10.1 dev eth0
实验
实验1 ping网关
admin@ubuntu:~$ sudo ip netns exec mac ping 10.10.10.1 PING 10.10.10.1 (10.10.10.1) 56(84) bytes of data. 64 bytes from 10.10.10.1: icmp_seq=1 ttl=64 time=0.050 ms --- 10.10.10.1 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.050/0.050/0.050/0.000 ms admin@ubuntu:~$ #查看邻居,是我们网关veth0的地址 admin@ubuntu:~$ sudo ip netns exec mac ip neigh 10.10.10.1 dev eth0 lladdr 00:01:02:03:04:05 REACHABLE admin@ubuntu:~$
实验2 修改邻居表,将网关地址改成系统中另外一个设备docker0的mac地址
admin@ubuntu:~$ ip link show docker0 7: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default link/ether 02:42:83:ca:a5:f3 brd ff:ff:ff:ff:ff:ff admin@ubuntu:~$ admin@ubuntu:~$ sudo ip netns exec mac ip neigh replace 10.10.10.1 lladdr 02:42:83:ca:a5:f3 dev eth0 admin@ubuntu:~$ sudo ip netns exec mac ip neigh 10.10.10.1 dev eth0 lladdr 02:42:83:ca:a5:f3 PERMANENT admin@ubuntu:~$ #继续ping网关,不可以ping通 admin@ubuntu:~$ sudo ip netns exec mac ping 10.10.10.1 -c 1 PING 10.10.10.1 (10.10.10.1) 56(84) bytes of data. --- 10.10.10.1 ping statistics --- 1 packets transmitted, 0 received, 100% packet loss, time 0ms admin@ubuntu:~$ #在端口上抓包可以看到目的mac为02:42:83:ca:a5:f3 admin@ubuntu:~$ sudo tcpdump -i veth1 -eevvnn tcpdump: listening on veth1, link-type EN10MB (Ethernet), capture size 262144 bytes 03:57:23.746932 aa:1b:28:83:ee:b5 > 02:42:83:ca:a5:f3, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 7402, offset 0, flags [DF], proto ICMP (1), length 84) 10.10.10.2 > 10.10.10.1: ICMP echo request, id 10034, seq 12, length 64 3 packets captured 3 packets received by filter 0 packets dropped by kernel admin@ubuntu:~$
实验3 在错误的mac地址下,ping docker0上的IP:172.17.0.1。即发送的报文是docker0的IP地址和mac地址
# 可以看到不能ping通 admin@ubuntu:~$ sudo ip netns exec mac ping 172.17.0.1 -c 1 PING 172.17.0.1 (172.17.0.1) 56(84) bytes of data. --- 172.17.0.1 ping statistics --- 1 packets transmitted, 0 received, 100% packet loss, time 0ms admin@ubuntu:~$ #报文如我们预期 admin@ubuntu:~$ sudo tcpdump -i veth1 -eevvnn tcpdump: listening on veth1, link-type EN10MB (Ethernet), capture size 262144 bytes 04:01:39.957123 aa:1b:28:83:ee:b5 > 02:42:83:ca:a5:f3, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 65477, offset 0, flags [DF], proto ICMP (1), length 84) 10.10.10.2 > 172.17.0.1: ICMP echo request, id 10113, seq 1, length 64
结论
在网卡开启混杂模式下,网卡可以接收目的MAC不是本机的报文,报文只会在链路层处理,不会送到网络层。即使对应的mac地址是系统中的另外一个网卡的mac地址。
查看代码
我们以intel的igb网卡为例进行分析,在该网卡的驱动代码中有如下函数:
/** * igb_process_skb_fields - Populate skb header fields from Rx descriptor * @rx_ring: rx descriptor ring packet is being transacted on * @rx_desc: pointer to the EOP Rx descriptor * @skb: pointer to current skb being populated * * This function checks the ring, descriptor, and packet information in * order to populate the hash, checksum, VLAN, timestamp, protocol, and * other fields within the skb. **/ static void igb_process_skb_fields(struct igb_ring *rx_ring, union e1000_adv_rx_desc *rx_desc, struct sk_buff *skb) { struct net_device *dev = rx_ring->netdev; igb_rx_hash(rx_ring, rx_desc, skb); igb_rx_checksum(rx_ring, rx_desc, skb); if (igb_test_staterr(rx_desc, E1000_RXDADV_STAT_TS) && !igb_test_staterr(rx_desc, E1000_RXDADV_STAT_TSIP)) igb_ptp_rx_rgtstamp(rx_ring->q_vector, skb); if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) && igb_test_staterr(rx_desc, E1000_RXD_STAT_VP)) { u16 vid; if (igb_test_staterr(rx_desc, E1000_RXDEXT_STATERR_LB) && test_bit(IGB_RING_FLAG_RX_LB_VLAN_BSWAP, &rx_ring->flags)) vid = be16_to_cpu(rx_desc->wb.upper.vlan); else vid = le16_to_cpu(rx_desc->wb.upper.vlan); __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid); } skb_record_rx_queue(skb, rx_ring->queue_index); //调用eth_type_trans函数确定网络层协议类型 skb->protocol = eth_type_trans(skb, rx_ring->netdev); }
/** * eth_type_trans - determine the packet's protocol ID. * @skb: received socket data * @dev: receiving network device * * The rule here is that we * assume 802.3 if the type field is short enough to be a length. * This is normal practice and works for any 'now in use' protocol. */ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev) { unsigned short _service_access_point; const unsigned short *sap; const struct ethhdr *eth; skb->dev = dev; skb_reset_mac_header(skb); eth = (struct ethhdr *)skb->data; skb_pull_inline(skb, ETH_HLEN); // 确定报文的类型,广播,组播,单播 if (unlikely(is_multicast_ether_addr_64bits(eth->h_dest))) { if (ether_addr_equal_64bits(eth->h_dest, dev->broadcast)) skb->pkt_type = PACKET_BROADCAST; else skb->pkt_type = PACKET_MULTICAST; } // 单播报文,比较是否与本设备的mac地址一致,不一致则设置skb->pkt_type = PACKET_OTHERHOST; else if (unlikely(!ether_addr_equal_64bits(eth->h_dest, dev->dev_addr))) skb->pkt_type = PACKET_OTHERHOST; ...... } EXPORT_SYMBOL(eth_type_trans);
从上面的代码可以看出,对于报文的目的MAC不是本机的单播报文会设置skb->pkt_type = PACKET_OTHERHOST。
我们继续看网络层如何处理该字段:
/* * Main IP Receive routine. */ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { const struct iphdr *iph; struct net *net; u32 len; /* When the interface is in promisc. mode, drop all the crap * that it receives, do not try to analyse it. * 对于skb->pkt_type == PACKET_OTHERHOST的报文(目的MAC不是本机)直接丢弃 * 而且在NF_INET_PRE_ROUTING节点之前。 */ if (skb->pkt_type == PACKET_OTHERHOST) goto drop; ...... return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, net, NULL, skb, dev, NULL, ip_rcv_finish); csum_error: __IP_INC_STATS(net, IPSTATS_MIB_CSUMERRORS); inhdr_error: __IP_INC_STATS(net, IPSTATS_MIB_INHDRERRORS); drop: kfree_skb(skb); out: return NET_RX_DROP; }