天天看点

arp/ip地址/路由--总之很乱

正常情况下,只要有到一台机器的路由,不管服务监听的哪个网卡的哪个地址,也不管请求从哪个网卡进入,只要这个请求的目的ip是该机器的一个ip,连接就能成功,当然你可以设置策略路由或者多表路由表项阻止的目的地址没有配置在数据进入网卡上的情况。

实例:

R有两块网卡,各个网卡以及环回口配置如下:

eth0:172.16.0.1

eth1:192.168.0.1

lo:127.0.0.1

lo:0:1.2.3.4

为了隐藏lo口上的ip,配置如下

sysctl -w net.ipv4.conf.lo.arp_ignore=2

PC有一块网卡,和R的eth0直连:

eth0:172.16.0.3

在PC上ping 1.2.3.4,不通。随后在PC上添加一条路由:

1.2.3.4 gw 192.168.0.1

然后再ping 1.2.3.4,通了。

arp_ignore的作用:

1.值为0--只要arp请求能被接收,均回复arp请求,mac地址为接收arp请求的网卡的mac

2.值为1--只有ip配置在arp请求进入的网卡上才回复

3.值为2--2的情况必须满足,并且arp请求的来源ip和请求ip属于同一个子网才回复

4.值为3--如果arp请求的ip是scope为host的,则不回复

5.值为7--一律不回复

注解:

1.scope的作用:scope表示一个范围,它是ip地址的属性,也就是说ip在该范围内是可用的,可被寻址的,内核的安排很好,枚举rt_scope_t实际上表示的是一个到目的地的“距离”,越往后数值越大,范围越小。比如127开头的ip只有本机可以使用,因此它的scope就是host,这个scope在为网卡配置ip地址的时候是可以设定的,如果你设定该ip的scope是host,那么这意味着很可能只有你自己机器内部需要这个ip,可以很方便的将arp_ignore设为4来对外隐藏这个ip地址。host地址可以用于负载均衡和实现虚拟网卡或者多网卡绑定,link地址用于直连主机,global可以随意使用。不仅仅ip地址有scope属性,路由也有scope属性,最后简述。

2.arp请求从哪个网口进入,那么arp回复则一定从该网卡出去,参见arp_process函数,因为入口dev也是路由查找键的一部分

源码级别:

1.值为0--直接返回“可回复arp请求入口的mac”

2.值为1--以dst = 0;scope = RT_SCOPE_HOST;调用inet_confirm_addr,由后者抉择

3.值为2--仅以scope为host调用inet_confirm_addr

4.值为3--以scope为link调用inet_confirm_addr

不管值为几,最终如果要回复arp请求的话,回复的都是arp请求入口的mac地址,这个arp_ignore函数仅仅判断“能不能回复”而已,它随着内核参数的配置不同,约束条件也不同。下面看一下inet_confirm_addr函数的实现:

u32 inet_confirm_addr(const struct net_device *dev, u32 dst, u32 local, int scope)

{

...

    if (dev) {   //对于接收到arp广播,将要发送arp单播回应的主机,dev严格是arp请求到来的dev,因此在dev被设置的情况下不能指望非arp请求进入网卡上的ip回应

        if ((in_dev = __in_dev_get_rcu(dev)))

            addr = confirm_addr_indev(in_dev, dst, local, scope);

        return addr;

    }

    for (dev = dev_base; dev; dev = dev->next) {  //如果没有指定dev,则遍历所有的dev,但是仍然不能指望配置在loopback上的一个ip地址回应,因为loopback根本就没有mac地址,并且既然它永远不会接收arp请求,它也就永远不会回复之,见实验0。

        if ((in_dev = __in_dev_get_rcu(dev))) {

            if (addr)

                break;

        }

    return addr;

}

实验0(注:在进行下一步的ping时,清掉arp缓存):

PC1的eth0:192.168.1.3  UP

PC1的eth1:172.16.1.3    没插网线

PC1的eth1:1:1.2.3.4 scope link

PC1的lo:1:4.3.2.1 scope link

PC2的eth0:192.168.1.4    UP 和PC1的eth0直连

1.PC1的eth0的arp_ignore设置为3

在PC2上ping 1.2.3.4,不通,ping 4.3.2.1,不通。

在PC2上添加到1.2.3.4和到4.3.2.1的路由,再ping,全通,并且arp查看到的二ip的mac地址为PC1的eth0的mac地址

2.PC1的eth0的arp_ignore设置为0

按照1重新ping,全部通,因为arp请求只要被接收就被回复

3.PC1的eth0的arp_ignore设置为1和2

按照1重新ping,全部不通,因为目的ip没有配置在接收arp请求的网卡上,另发送者的ip和ping的目的地址也没有在一个子网

4.PC1的eth0的arp_ignore设置为3,但是重新如下配置:

PC1的eth1:1:1.2.3.4 scope host

PC2上ping 1.2.3.4,不通,因为arp_ignore为3的话虽然不检查网卡设备和ip的关联性,但是却不允许回复scope为host的地址

     最后通过上述的分析以SO_DONTROUTE选项为基准来分析一下路由的scope属性。路由的scope属性表示目的地址距离本地的远近,和ip地址的scope一样,也分为host,link,global之类的,在通过ip route命令添加路由的时候,如果为一条路由添加了网关(通过via参数),那么你就无法使用scope link参数,也就是说通过网关进行路由(via)和直连路由只能采取一种,因此在路由添加的时候,这个约束条件使得scope不会混乱。而SO_DONTROUTE参数的实质就是不通过网关进行路由,因此带有该选项的连接发送数据前查找路由时仅会查找scope为link的表项,如果你不相信dontroute的行为真的是这样,你可能会想办法设置一条路由,然后逼着dontroute的数据包走向你设置的路由,也就是给它一个“下一跳”,而不让它直接到达目的地,那么你能把这条路由加入吗?如果你设置了via nexthop参数,那么scope就不能为link,因此查找路由就会失败,然后转入直接发送,如果你添加了一条直连路由,那么很好,路由查找不会失败,协议栈会使用查找到的结果。因为dontroute的策略是查到路由就按照路由发送,查不到就直接发送,前提设置了一个出口网卡设备,带有dontroute标志的套结字发送的数据包不是不查找路由,相反它还可能会找到一条路由并使用它,如果它是直连路由的话。

 本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1271756

继续阅读