目录
一、概述
一般情况下是不这么配置的,但是实际项目中总有些奇葩的甲方。在虚拟机中模拟一下这个情形。
二、分析
虚拟机中添加三个网卡,ip地址配置成同一个网段,桥接。主机的IP地址是192.168.0.109
ens33: inet 192.168.0.112 netmask 255.255.255.0 broadcast 192.168.0.255
ether 00:0c:29:ae:cd:e2 txqueuelen 1000 (Ethernet)ens37: inet 192.168.0.111 netmask 255.255.255.0 broadcast 192.168.0.255
ether 00:0c:29:ae:cd:ec txqueuelen 1000 (Ethernet)ens38: inet 192.168.0.107 netmask 255.255.255.0 broadcast 192.168.0.255
ether 00:0c:29:ae:cd:f6 txqueuelen 1000 (Ethernet)
试着在主机上分别ping三个接口,都是通的,但是此时看mac表:
三个IP的mac都是一样的,这是为什么呢,在解释原因之前,我们先看下虚拟机上的路由,可以发现,三个接口添加了三条直连路由:
[root@localhost ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
192.168.0.0 0.0.0.0 255.255.255.0 U 0 0 0 ens38
192.168.0.0 0.0.0.0 255.255.255.0 U 100 0 0 ens33
192.168.0.0 0.0.0.0 255.255.255.0 U 101 0 0 ens37
这种情况下只有一条路由生效,可以使用ping 192.168.0.109发现是通的(通过抓包,发现源IP是192.168.0.108,即是从ens38口发出的;对应的ens38 metric最小,优先级最高)。
回到开始的问题,在主机上分别ping虚拟机上三个网口时,会发送arp request,对于我们目前的组网来说,每个网口都会收到arp广播,但是抓包可以发现,所有的arp都是回复的ens38口的MAC,这就是主机上对应三个虚拟机网口地址一样的直接原因。
虚拟机在arp reply的时候要查找路由,由于三个地址都是本机地址,所以从代码看arp对于这三个地址应该有三个回复,
if (arp->ar_op == htons(ARPOP_REQUEST) &&
ip_route_input_noref(skb, tip, sip, 0, dev) == 0) {rt = skb_rtable(skb);
addr_type = rt->rt_type;if (addr_type == RTN_LOCAL) {
int dont_send;dont_send = arp_ignore(in_dev, sip, tip);
if (!dont_send && IN_DEV_ARPFILTER(in_dev))
dont_send = arp_filter(sip, tip, dev);
if (!dont_send) {
n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
if (n) {
arp_send_dst(ARPOP_REPLY, ETH_P_ARP,
sip, dev, tip, sha,
dev->dev_addr, sha,
reply_dst);
neigh_release(n);
}
}
但实际过程中只有一个arp reply,可以用systemtap跟踪一下每个口报文的情况,这里我发现除了ens38能成功发送arp reply,其他的口对应的arp request处理在查路由时失败了,关键就在于fib_validate_source函数,fib_validate_source的逻辑是这样,如果查路由时发现目的IP是local的,要进一步进行校验,主要的方法是将sip,dip对调,再去查一遍路由,这时候要保证二者查到的接口是一致的,这就是所谓反向过滤(rp_filter)配置,你可以试着关闭,可以看到三个arp reply,虽然这也不是好事情。
if (res.type == RTN_LOCAL) {
err = fib_validate_source(skb, saddr, daddr, tos,
0, dev, in_dev, &itag);
if (err < 0)
goto martian_source;
goto local_input;
}
通过上面的分析,在arp reply进行反向路由查找校验时,由于源地址是192.168.0.109,查找时一定会命中第一条直连路由,于是将ens38对应的mac回复给host。而在其他端口发送相应的arp request时就会产生冲突。
那么如果想要通过ens37访问192.168.0.109如何实现呢,我们使用ping 192.168.0.109 -I ens37,这时候没有通,再抓包,发现这次报文确实是从ens37发出的,当使用ens37 ping host时,报文发送的源mac确实是ens37的,从主机上抓包可以确认:
按照上面的分析 host回复报文目的mac是ens38的:
这个报文由于目的mac是ens38的从而导致监听ens37端口的ping无法返回。
由上我们知道,处于相同网段的几个IP之间,在进行arp reply的时候,总会使用优先级高的那条直连路由对应的mac,导致后续回包的目的MAC和目的IP不匹配,导致接收失败。如果主机上学到三个接口正确的MAC地址,就不会产生这种情况。
一种解决的方法是使用策略路由,配置如下:
- ip rule add from 192.168.0.111 table 20
- ip route add default via 192.168.0.1 dev ens37 table 20
- ip route add 192.168.0.0/24 dev ens37 src 192.168.0.111 table 20
目的是配置在同一网段ip时产生的直连路由不产生冲突。如主机请求ens37的mac时,在arp reply时查反向路由,由于src ip匹配了,会去table 20中查找路由,命中table 20中的直连路由,从而以ens37的mac进行回应(在未配置策略路由的情况下,到ens38的广播报文总是匹配优先级高的路由),ens37的目的mac正确时就能正常转发到网卡ens37了。