IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

在LVS上实现SNAT网关

UC技术博客 2013-10-08 12:38:25 累计浏览 2,350 次
本机暂存

   最近研究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不会帮我们做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.rpm
cd rpmbuild/SPECS
rpmbuild -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 -j16
make modules_install
make install
#重启
init 6
#grub选择新内核

   6. LVS工具安装

#进入刚才clone的dsnat代码目录下
cd dsnat/dsnat-kernel-2.6.32-279.el6/dsnat_tools/keepalived
make && make install
cd ../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_64
make 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 += -DDEBUG
endif

   9. 重新编译安装ipvs模块

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

   10. 查看ipvs模块调试日志

#修改ipvs调试日志级别,默认为0
echo 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 service
ipvsadm -A -t 0.0.0.0:0 -s rr

#添加一个内网源地址网段,做源地址匹配,符合该网段的才做SNAT
ipvsadm -K  --zone 192.168.0.0/16

#为192.168.0.0/24添加一个local address,即映射后的外网地址,可以加多个,轮询使用
#注意dsnat中使用-P来添加zone的laddr,这里改为-U
ipvsadm -U --zone 192.168.0.0/24 -z 121.14.161.100
ipvsadm -U --zone 192.168.0.0/24 -z 121.14.161.101

#删除zone的laddr由-Q改为-W
ipvsadm -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,增加一些功能,对其理解会更加透彻,对今后分析解决相关问题会很有帮助。

同分类推荐文章

  1. 从零重建 macOS 开发机:可复现的环境初始化流程 (2026-06-14 20:36:00)
  2. 百度物理网络监控工具开源第二弹:毫秒级监控工具 baize,让你的网络问题无处遁形 (2026-06-11 08:10:28)
  3. How to Set Up Homebrew Tap for Private CLI Tools: A Complete Guide (2026-05-27 02:13:03)

查看更多 DevOps 文章 →

建议继续学习

  1. 解析nginx负载均衡 (累计阅读 16,622)
  2. Facebook 网站架构 (累计阅读 11,112)
  3. 使用Apache 和Passenger来运行puppetmaster (累计阅读 8,315)
  4. LVS hash size解决4096个并发的问题 (累计阅读 6,408)
  5. 由12306.cn谈谈网站性能技术 (累计阅读 6,398)
  6. NAT连通性测试工具以及Flash P2P中的NAT穿透原理 (累计阅读 5,701)
  7. LVS & MySQL NDB Cluster (累计阅读 5,114)
  8. 记一次LVS/Nginx环境下的访问控制 (累计阅读 5,069)
  9. 什么是NAT (累计阅读 5,009)
  10. LVS & MySQL NDB Cluster (累计阅读 4,967)