NA(192.168.1.0/24)--(eth0)R(eth1)--NB(192.168.2.0/24)
a(eth0:192.168.1.2)--(eth0:192.168.1.1)R(eth1:192.168.2.1)--b(eth0:192.168.2.2)
一:a配置指向NB的路由的情况:
在R开启arp代理的情况下如果在a上ping b,结果是什么,显然数据要发送出去首先需要arp,此时arp请求的mac地址实际上R的mac地址,
并不需要arp代理功能,因为有路由,ping包在出去前就已经得知下一跳是R,因此arp会直接请求R的mac地址。
二:a没有配置路由的情况:
此时如果直接ping的话,会得到“没有路由”的错误信息,也就是说数据在出去的过程中,查询路由这一步就会失败,arp请求是根本不会发出
的,因此R的arp代理根本不会影响到这次ping。
问题:那么怎样使得R的arp代理其作用呢?
答案:这个问题可以转化为如何发出arp请求的问题,只要arp发出了,接下来必然要传输数据了,再者,arp对于上层是不可见的。两种方式,
一种是将a的ip地址掩码设为n位,n为a和b从最高位开始一直保持一致的位数,这样a会认为b和自己在一个网段,arp最终会发出去,并且直
接请求b的mac地址;第二种情况就是不使用ping,而是自己写一个发送程序,设置SO_DONTROUTE标志并且绑定一个出口设备即可,这样
的话,无论有没有路由,数据都会按照既定的出口发出。上述两种情况下,R均会收到这个arp请求,此时arp代理将要起作用了。
R会检查自己的路由表,看看自己能否处理这个arp请求包中的ip地址,如果能的话,R会回复自己的mac地址来代理b或者说欺骗a,注意,
R会查找自己的路由表,看看自己能否处理或者代理处理,linux的内核使用了一种一致的方式:首先arp_process会调用ip_route_input来
以arp请求包中的ip作为查找键来查找路由表,接着要么有个结果,要么没有查到,没有查到的话就不会发送arp回应,仅仅更新自己的arp表,
如果查到的话分为两种情况,一种情况是这个ip就是自己的一个ip,另一种情况是这个ip不是自己的ip,linux的代码如下(arp_process函数): if (addr_type == RTN_LOCAL) { //如果自己有这个ip地址 (***)
n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
if (n) {
...//可能的话发送arp回复
} else if (IN_DEV_FORWARD(in_dev)) { //如果请求的ip地址不是自己的,但是自己可能代理之
if ((rt->rt_flags&RTCF_DNAT) ||
(addr_type == RTN_UNICAST && rt->u.dst.dev != dev && //注意这里的设备判断,如果查找到的出口和arp请求的入口是一个设备的话就不必发送arp代理回应了,因为这样没有意义
(arp_fwd_proxy(in_dev, rt) || pneigh_lookup(&arp_tbl, &tip, dev, 0)))) {
...//可能的话发送arp代理的回复
}
}
因此,只要arp请求能够发送到R,R的arp_process总是会有所动作的,要么仅仅回复自己的mac地址,要么不回复自己的mac地址。不管在
arp请求中ip地址是自己的情况还是虽然不是自己的但是自己可以代理的情况,R都会发送自己的mac地址作为arp回应的。可是,有个对ip地
址配置理解很深的家伙破坏了这一切。
他知道一个设备可以配置多个ip地址,于是他索性为a的eth0配置了一个192.168.2.3,这样a和b不就在同一个网段了吗?但是他不知道,
任何逻辑的东西必然需要物理设施的支撑。我们可以在同一物理网段随意配置不同的逻辑网段,但是反过来我们却不能在不同的物理网段配置
同一个逻辑网段。下面看一下发生了什么:由于linux是先查找路由再填写源ip的(将源ip写成出口设备的ip,并且尽量[忽略复杂情况]和下一跳
在同一网段),那么route模块肯定会将源ip写成192.168.2.3,于是将192.168.2.3作为arp请求发送出去了,R收到后会以192.168.2.3进
行路由,不考虑细节的情况下或者对路由不求甚解的情况下会得出这个这条路由在eth1一端的结论,可是,这是错误的结论,正确的结论是根
本不会找到路由。因为: 添加一条路由的时候,该路由项会绑定一个设备(inet_rtm_newrule函数):
new_r->r_ifname[IFNAMSIZ-1] = 0;
new_r->r_ifindex = -1;
dev = __dev_get_by_name(new_r->r_ifname);
if (dev)
new_r->r_ifindex = dev->ifindex;
一条路由决定了一个目的地的出口为该设备,反过来任何从这个目的地网段来的数据包必须从该设备进入,这就是路由器隔离网段的实质。如
果一个路由器有eth0和eth1两个网口,通往NET1的数据从eth0出去,此时从eth1进来一个NET1的包,那是不合法的。既然数据通信必然
需要链路层解析arp,因此只要arp逻辑卡得严格的话就能起到隔离网段的作用,上述(***)完全做到了这一点,ip_route_input要么因为
arp的入口网卡设备和路由的出口网卡设备不一致而失败,要么查找到路由,该路由的出口设备正是arp请求的入口设备,然后
rt->u.dst.dev != dev判断将会失败,只有在路由查找的结果就是本机地址或者完全符合代理条件时才有可能发送arp回应,确保设备
(主机/路由器等三层以及三层以上设备)接收到和自己某个ip同一网段的arp请求时,仅在和自己某个ip相等时才回应,并且只能从收到arp请求
的设备回应,在设备接收到不同网段的arp请求时,会代理回复arp回应,并且被代理的ip的路由出口设备一定不是arp请求的入口设备。因此
不能指望将两个由一台路由器连接的不同物理网段配制成一个逻辑网段而实现链路层意义的互通(通过直接或者代理的arp),必须使用路由配
置这样的三层机制才能实现互通。