如果一个主机绑定有多个IP地址,那么在被动响应和主动发起连接两种方式中,源IP地址的选择机制肯定是有所差异的。主机在接收外部数据包,并发送响应数据包时,响应源地址显然就是客户端请求的地址,这是非常容易理解的,如客户端向主机的1.1.2.3:80发起请求,那么主机响应数据包的源IP地址一定是1.1.2.3。

那么当主机对外主动发起请求时,数据包的源IP地址如何选择?这个问题我们可能一般很少深入了解, 为了弄清楚这个问题,我发了不少时间,广泛查阅各种资料,目前得出的结论如下:

Linux 2.2 选择源IP的三种机制

    1. 应用程序可以通过bind(2)系统调用,应用至sendmsg(2)调用上,并通过辅助数据对象IP_PKTINFO,从而显式指定源IP地址。在这种情况下,操作系统内核仅仅检查其源IP地址是否正确,否则产生相应的错误。
    1. 如果应用程序没有指定源IP地址,包含源IP的路由表将决定数据包源IP地址,通过设置ip route命令的src参数,从而指定源IP地址。如果路由表没有包含src属性,则使用主要IP地址。
    1. 其它情况下内核搜寻绑定定数据包路由接口上的IP地址,IPv6选择第一个可用的IP地址。IPv4情况下,尽量选择与目标IP处于同一子网的源IP,如果目标IP与自己的所有ip没有处于同一子网,则使用第二种算法。

相同/不同子网情况

默认情况下,如果Linux的网卡有多个IP且位于不同的子网之中,如果数据包目标地址为某个子网中的IP, 那么对应的与目标同子网的IP将会被使用。如果eth0有两个IP 192.168.1.12/24, 10.1.1.1/8,那么到10.0.0.0子网的数据包的源地址将使用10.1.1.1。 当然可以使用ip route的src属性指定源址。

如果绑定的几个IP处于同一个子网内,那么主要IP地址将被使用(如eth0接口上的IP), 也可以使用iptables修改数据包的源地址实现之,如:

iptables -t nat -I POSTROUTING -o eth0 -d 1.2.3.4/0 -s 192.168.100.1 -j SNAT --to-source 192.168.100.2

多IP指定出口ip实例

原理分析及处理办法我们已经分析完毕,接下来使用实际的例子展示

目标: 当此主机对外发起新连接时,源IP地址使用192.168.0.22, 不使用默认的192.168.0.250。

环境: Linux 主机绑定有以下几个IP(网关为192.168.0.1)
eth0 192.168.0.250/24, eth0: 1 192.168.0.22/24, eth0:2 192.168.0.23/24

另外,绑定多个IP可使用ip addr add命令,不产生子接口。

在上述案例中192.168.0.250将成为默认主要IP。

方法一:修改路由表的源IP属性
一. 查看系统的 ip 地址及路由表详细信息 ( 加粗字体是输入的内容 )

**[**root**@**localhost **~**]**# ip addr**

 

**1**: **lo**: **<**LOOPBACK**,**UP**,**LOWER_UP**> **mtu **16436 **qdisc **noqueue**

 

**link**/**loopback **00**:**00**:**00**:**00**:**00**:**00 **brd **00**:**00**:**00**:**00**:**00**:**00

 

**inet **127.0.0.1**/**8 **scope **host **lo**

 

**inet **192.168.100.250**/**32 **scope **global **lo**

 

**2**: **eth0**: **<**BROADCAST**,**MULTICAST**,**UP**,**LOWER_UP**> **mtu **1500 **qdisc **pfifo**_fast **qlen **1000

 

**link**/**ether **f4**:**6d**:**04**:**76**:**ca**:**98 **brd **ff**:**ff**:**ff**:**ff**:**ff**:**ff

 

**inet **192.168.0.250**/**24 **brd **192.168.0.255 **scope **global **eth0**

 

**inet **172.16.25.1**/**24 **scope **global **eth0**

 

**inet **192.168.0.22**/**24 **scope **global **secondary **eth0

 

**inet **192.168.0.23**/**24 **scope **global **secondary **eth0

 

请注意 **192.168.0.250是**global状态,而其它地址是**secondary状态。**
**[**root**@**localhost **~**]**# ip route**

 

**192.168.0.0**/**24 **dev **eth0  **proto **kernel  **scope **link  **src **192.168.0.250**

 

**172.16.25.0**/**24 **dev **eth0  **proto **kernel  **scope **link  **src **172.16.25.1**

 

**169.254.0.0**/**16 **dev **eth0  **scope **link**

 

**default **via **192.168.0.1 **dev **eth0**

注意以上输出,会发现到同一子网的路由的源IP地址会使用主要IP地址。而到默认网关的路由没有指定源IP(实际上会用与网关同一子网的主要IP)。
修改路由表,让系统使用指定IP(192.168.0.22)作为源址:

**[**root**@**localhost **~**]**# ip route change default dev eth0 src 192.168.0.22**

 

**[**root**@**localhost **~**]**# ip route change to 192.168.0.0/24 dev eth0 src 192.168.0.22**

 

**[**root**@**localhost **~**]**# ip route**

 

**192.168.0.0**/**24 **dev **eth0  **scope **link  **src **192.168.0.22**

 

**172.16.25.0**/**24 **dev **eth0  **proto **kernel  **scope **link  **src **172.16.25.1**

 

**169.254.0.0**/**16 **dev **eth0  **scope **link**

 

**default **dev **eth0  **scope **link  **src **192.168.0.22**

方法二,使用iptables修改源IP地址:

**iptables **-**t **nat **-**I **POSTROUTING **-**o **eth0 **-**d **0.0.0.0**/**0 **-**s **192.168.0.250 **-**j **SNAT **--**to**-**source **192.168.0.22

方法二比较简单,大家可以写好脚本来切换出口IP地址

Last modification:June 25, 2022
如果觉得我的文章对你有用,请随意赞赏