在LVS上实现SNAT网关

最近研究LVS,给其增加了SNAT网关的功能,见https://github.com/jlijian3/lvs-snat,下面跟大家分享一下学习心得。


LVS简介:

LVS是Linux Virtual Server的简写,即Linux虚拟服务器,功能是4层反向代理负载均衡。 1998年5月由章文嵩博士开源。 2004年12月25日做为netfilter模块加入官方内核2.6.10。 站点:http://www.linuxvirtualserver.org/ http://zh.linuxvirtualserver.org/


研究目的:

使用LVS做4层反向代理负载均衡 在网关做SNAT,为内网机器提供访问外网的功能

假设大家已经了解LVS的转发方式,这里仅叙述分析和解决问题的过程。


使用场景:

    反向代理使用LVS的NAT转发模式

    LVS机器有两张网卡,一张绑定外网IP,另一张绑定内网IP; 后端机器跟LVS内网IP处于同一个内网网段,每台后端机器都只有一个内网地址,他们的网关都指向LVS机器内网IP。

    在LVS机器上做SNAT网关

    内网机器访问外网时(比如访问www.baidu.com),数据包会经过LVS机器转发出去,如果想正常访问要做以下事情,即SNAT: 1)把数据包的源ip由内网地址改为LVS机器的外网地址,再转发出去; 2)收到外网服务器的应答时,需要把目的地址由LVS机器的外网地址改为内网机器地址,再转发到内网机器,如下图:


问题:

LVS不会帮我们做SNAT,它只做反向代理,只处理客户端请求和来自内网机器的应答数据。 一个解决方法是在LVS机器上启动iptables做SNAT,如:

iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j SNAT --to-source 121.14.161.100

但是如果启用iptables,即使内网机器不访问外网,也会严重影响到LVS反向代理的性能,因为每个应答数据包都有经过iptables(后来把单队列网卡升级为多队列网卡,发现iptables对LVS的性能影响很小,这里不展开分析)。

另一个解决方法是修改LVS源代码实现SNAT网关。


以学习研究LVS为目的,为LVS增加SNAT功能:

幸运的是小米的同学做了这件事情,https://github.com/xiaomi-sa/dsnat。 他在LVS的FULLNAT基础上,增加了SNAT网关功能,使内网机器可以访问外网。 可惜他没有考虑跟反向代理的兼容性,NAT/FULLNAT转发方式无法正常使用,FULLNAT的配置项被修改用于SNAT的配置。

接下来要做两件事情: 1)修复小米dsnat问题,使反向代理和SNAT可以同时使用;2)直接在官方内核版本NAT转发模式上添加SNAT网功能,因为小米dsnat依赖于淘宝的FULLNAT补丁,我们并不需要FULLNAT。

1. 下载redhat 6.3内核

wget ftp://ftp.redhat.com/pub/redhat/linux/enterprise/6Server/en/os/SRPMS/kernel-2.6.32-279.el6.src.rpm

2. 准备代码

rpm -ivh kernel-2.6.32-279.23.1.el6.src.rpmcd rpmbuild/SPECSrpmbuild -bp kernel.spec

3. 获取dsnat内核补丁和lvs tools代码

git clone git@github.com:xiaomi-sa/dsnat.git

4. 打补丁

cd ~/rpmbuild/BUILD/kernal-2.6.32-279.23.1.el6/linux-2.6.32-279.23.1.el6.x86_64#把dsnat内核补丁拷贝到当前目录cp ~/dsnat/dsnat-kernel-2.6.32-279.el6/dsnat-2.6.32-279.el6.xiaomi.noconfig.patch ./patch -p1 < dsnat-2.6.32-279.el6.xiaomi.noconfig.patch

5. 编译安装

make -j16make modules_installmake install#重启init 6#grub选择新内核

6. LVS工具安装

#进入刚才clone的dsnat代码目录下cd dsnat/dsnat-kernel-2.6.32-279.el6/dsnat_tools/keepalivedmake && make installcd ../ipvsadm/make && make install

7. 打开ipvs模块调试开关

cd ~/rpmbuild/BUILD/kernal-2.6.32-279.23.1.el6/linux-2.6.32-279.23.1.el6.x86_64make menuconfig进入Networking support->Networking options->Netfilter->IP Virtual server support,勾选IP Virtual server debugging

8. 打开pr_debug开关

#LVS很多地方使用pr_debug打日志,需要编译选项加上-DDEBUG#修改net/netfilter/ipvs/Makefile,增加ifeq ($(CONFIG_IP_VS_DEBUG),y)    EXTRA_CFLAGS += -DDEBUGendif

9. 重新编译安装ipvs模块

#进入内核代码目录,编译模块make M=`pwd`/net/netfilter/ipvs modules#卸载模块ipvsadm -Clsmod|grep ip_vsrmmod ip_vs_rrrmmod ip_vs#更新ko文件cp net/netfilter/ipvs/*.ko /lib/modules/`uname -r`/kernel/net/netfilter/ipvs#重启lvs

10. 查看ipvs模块调试日志

#修改ipvs调试日志级别,默认为0echo 12 > /proc/sys/net/ipv4/vs/debulg_level#查看日志dmesg|grep IPVS|less#如果想直接在屏幕输出echo 8 > /proc/sys/kernel/printk

11. 使用tcpdump分析问题

#查看各个网卡的数据tcpdump -X -n -i eth0

12. 修复dsnat跟NAT的兼容性bug

ipvs代码路径:net/netfilter/ipvs 在ip_vs_core.c中找到forward钩子函数ip_vs_out,内网机器访问外网的请求数据、NAT应答数据都经过此钩子函数。 dsnat的作者没有区分这两种数据包,错误的修改了NAT的应答数据包的目的地址。 解决方法:因为dsnat是通过添加一个0.0.0.0:0的service来做SNAT的,在做dsnat之前,判断svc->addr.ip和svc->port是否为0,否则走NAT应答数据处理流程。 详情见https://github.com/jlijian3/lvs-snat

13. 修复dsnat跟FULLNAT的兼容性bug

dsnat作者没有考虑跟FULLNAT兼容,直接修改了FULLNAT的local address配置参数,用来添加SNAT的映射地址。 而我们希望两者同时使用。接下来要做的就是恢复FULLNAT的local address的添加和使用方式。 不仅要修改ipvs代码,还有修改ipvsadm和keepalived,开发和调试比想象中的麻烦。 详情见https://github.com/jlijian3/lvs-snat

14. 不使用FULLNAT补丁,在官方内核上实现SNAT

小米的dsnat基于淘宝开源的FULLNAT,但是我们不需要FULLNAT转发功能,接下来从头开始,不打dsnat补丁,直接在NAT转发基础上修改,改动量很少,ipvsadm和keepalived不需要修改,但是不支持源地址白名单过滤。 详情见https://github.com/jlijian3/lvs-snat

15. 其他问题

SNAT网关目前只支持tcp和udp,icmp的转发还不支持,在内网ping 外网ip是ping不同的,今后有时间再增加对icmp的支持。

16. 生成补丁

调试测试ok,准备两份代码,修改前和修改后的

diff -rupN linux-2.6.32-279.el6 linux-2.6.32-279.el6.snat > lvs-snat-2.6.32-279.el6.patch

17. 基于dsnat的SNAT网关ipvsadm配置示例

#添加一个0/0的virtul serviceipvsadm –A –t 0.0.0.0:0 –s rr#添加一个内网源地址网段,做源地址匹配,符合该网段的才做SNATipvsadm -K  --zone 192.168.0.0/16#为192.168.0.0/24添加一个local address,即映射后的外网地址,可以加多个,轮询使用#注意dsnat中使用-P来添加zone的laddr,这里改为-Uipvsadm -U --zone 192.168.0.0/24 -z 121.14.161.100ipvsadm -U --zone 192.168.0.0/24 -z 121.14.161.101#删除zone的laddr由-Q改为-Wipvsadm -W --zone 192.168.0.0/24 -z 121.14.161.100#-P/-Q恢复为原来的功能,即为FULLNAT的service添加删除laddr

18. 基于NAT的SNAT网关的ipvsadm配置示例

#直接用官方的ipvsadm即可,功能简单,没有源地址网段匹配,只要不是反向代理转发的数据,就做SNAT#添加0.0.0.0:0的虚拟服务,加上-p参数#因为只有persistent service才能添加端口为0的服务,而我懒得修改ipvsadm代码了ipvsadm -A -t 0.0.0.0:0 -s rr -p 10#添加转换后的源地址,这里直接使用添加real server参数,端口为0,如下ipvsadm -a -t 0.0.0.0:0 -r 121.14.161.100:0 -m#内网访问外网时,源地址就会被改为121.14.161.100

小结:

linux内核模块开发调试跟应用程序的开发还是很不一样的,调试起来要麻烦一些,不过只要有耐心,多查点资料,还是不难搞定的。 在学习研究开源软件时,如果能为其修复一些bug,增加一些功能,对其理解会更加透彻,对今后分析解决相关问题会很有帮助。

在LVS上实现SNAT网关

相关文章:

你感兴趣的文章:

标签云: