利用iptables修改UDP报文源IP地址的方法

来源:互联网 发布:手机淘宝旧版5.2.2 编辑:程序博客网 时间:2024/05/21 09:33

1. 问题起因:

最近在一个项目中,有一个模块是基于UDP的服务端,为了保证系统的高可用性,方案中采用了KeepAlive做主备切换,拓扑模型如下:




客户端访问系统的入口地址是VIP,此时VIP落在UDP Server主上,也就是说,此时实际上UDP Server主有两个IP,一个是自己的实际IP地址,即IP1,另一个是VIP。理想情况下,系统是通过VIP接受了UDP报文,回报文的时候也应该是通过VIP回。但是不幸的是,系统实际以IP1回复了。客户端发送的IP地址和接受报文的IP地址不一致,造成客户端不识别报文。


2. 解决方案

为了在不修改代码的情况下解决这个问题,笔者选择了通过linux的iptables报文转发功能来实现修改报文的源IP地址的目的,但解决的过程比较曲折,下面笔者跟大家分享下整个过程,以加深对iptables的理解。


本实验中VIP=10.0.255.33,实际的IP1=10.0.255.2,服务器的监控端口为2000

客户端将报文发送到VIP(10.0.255.33),但系统确以IP1(10.0.255.2)回复,从而造成通信失败。

此时,笔者直接想到的解决方案是,通过iptables修改出口报文的IP地址,语句如下:

-A POSTROUTING -s 10.0.255.0/24 -p udp -mudp --sport 2000 -j SNAT --to-source 10.0.255.33:2000

实验中,抓包发现,回复报文的IP地址是对了,成功从IP1(10.0.255.2)修改成为VIP(10.0.255.2),本以为大功告成了,但又产生了新的问题!回复的端口号不对了!

端口号本应该以2000作为源端口发出去的,但是确以2001发出去了,但我在规则里写明了是以2000发出去啊。

思索再三,终于找出了原因,iptables修改报文的IP地址,也能修改端口号,但是修改后的端口号绝对不能是已经被监听的端口号,即便该报文本来就是从监听该端口号的进程发出来的。

根据以上思路,笔者决定,修改监听服务的端口号为2001端口,报文进来的时候,通过POSTROUTING重定向UDP 2000的报文到2001端口,而从2001端口发出去的报文再通过POSTROUTING修改报文的源IP地址和端口号,此时的端口号重新修改到2000.而对于客户端并不会感知到这些行为,客户端从头到尾都是与一个UDP 2000的服务器进行通信。

下面是实验的iptables配置

# Generated by iptables-save v1.4.7 on FriDec  2 13:23:33 2016

*filter

:INPUT ACCEPT [439:20767]

:FORWARD ACCEPT [0:0]

:OUTPUT ACCEPT [3849:433903]

-A INPUT -m state --stateRELATED,ESTABLISHED -j ACCEPT

-A INPUT -p icmp -j ACCEPT

-A INPUT -i lo -j ACCEPT

-A INPUT -p tcp -m state --state NEW -m tcp--dport 22 -j ACCEPT

-A INPUT -p tcp -m state --state NEW -m tcp--dport 80 -j ACCEPT

-A INPUT -p tcp -m state --state NEW -m tcp--dport 3000 -j ACCEPT

-A INPUT -p udp -m udp --dport 3799 -jACCEPT

COMMIT

# Completed on Fri Dec  2 13:23:332016

# Generated by iptables-save v1.4.7 on FriDec  2 13:23:33 2016

*nat

:PREROUTING ACCEPT [10:3664]

:POSTROUTING ACCEPT [475:41448]

:OUTPUT ACCEPT [475:41448]

-A PREROUTING -p udp -m udp --dport 2000 -jREDIRECT --to-ports 2001

-A POSTROUTING -s 10.0.255.0/24 -p udp -mudp --sport 2001 -j SNAT --to-source 10.0.255.33:2000

COMMIT


整个配置的关键在标红色的两行,下面分别讲解

-A PREROUTING -p udp -m udp --dport 2000 -jREDIRECT --to-ports 2001

此处PREROUTING的作用是修改人口的报文,此处的作用是将入口的2000端口的报文重定向到2001

-A POSTROUTING -s 10.0.255.0/24 -p udp -mudp --sport 2001 -j SNAT --to-source 10.0.255.33:2000

此处POSTROUTING的作用是有两个:修改源IP地址为VIP(10.0.255.33)和将源端口号重新修改为2000


经过以上的曲折配置,实验终于成功!