linux中,如果在一张网卡上配置多个同一网段的ip,那么除了第一个被配置的之外,其余的都将是secondary IP。如果一个udp服务器bind了0.0.0.0地址,而一个udp客户端来连接一个secondary IP,那么连接是不会正常的,抓包会发现udp服务器的回复包的源ip是一个primary IP,而不是它所连接的secondary IP。对于tcp则不存在这样的问题。这是linux内核协议栈所决定的。
对于tcp而言,监听0.0.0.0会导致它接受所有连接本机ip地址的客户端,只要这个ip在本机即可,这是通过路由得知的,只要数据包越过了网络层,到达了tcp_v4_rcv并且成功在监听套接字中找到了端口,那么服务器将建立一个通信套接字,该套接字的源ip完全对应于客户端的目的ip,而对于监听0.0.0.0的udp服务器而言,由于它是没有连接的,在bind的时候,inet数据结构的saddr字段就是0.0.0.0,因此只有在服务器实际回复数据的时候到达网络层填写ip头的时候才会确定回复包的源地址,此时,协议栈已经完全忘记了来源包的目的地址(udp无连接),因此路由完毕找到了出口的时候,如果该条路由没有源地址信息,那么将会调用inet_select_addr来选择一个ip地址,这个函数选择ip的策略就是首先在路由结果的出口设备上按照一定原则找primary地址,如果找不到,则在所有的设备上按照一定原则找primary地址,可见secondary地址是无论如何都不能被选中的。
使用OpenVPN可以做一个测试,udp情况下设置服务器bind 0.0.0.0 1194,然后为服务器添加一个secondary IP-1.2.3.5/24,然后客户端连接之,会出现下面的出错报告:
Incoming packet rejected from 1.2.3.4:1194[2], expected peer address: 1.2.3.5:1194 (allow this incoming source address/port by removing --remote or adding --float)
可见事实。如果加上float就可以了,这个float并不是为了解决secondary IP的问题的,其实这并不是一个问题,这只是一个机制而已。float是为了解决服务器ip地址变化问题的,这里只是歪打正着的解决了secondary IP所谓的问题。