KVM下新建虚拟机,为了节省公网IP地址,将公网IP配在了物理主机上,内部的虚拟机通过nat端口映射来共用公网IP。由于平时像tomcat、nginx等应用都是监听的tcp端口 ,一般在iptables做映射时只需一条目的地址转换就OK了。今天突然一位开发的同事使用的公网上的一台测试机需要udp端口映射 。本以为也像tcp一样, 只需要将命令中的tcp改为udp就OK了,增加后却发现无效。具体如下

一、通过iptables配置转发

1#开启路由转发
2echo "1">/proc/sys/net/ipv4/ip_forward
3#增加目的地址转换
4iptables -t nat -A PREROUTING -d 111.225.101.188 -i br1 -p tcp -m tcp --dport 6464 -j DNAT --to-destination 192.168.122.31:6464
5#增加源地址转换
6iptables -t nat -A POSTROUTING -s 192.168.122.31 -p udp --dport 6464 -j SNAT --to-source 111.225.101.188

开启路由转发这条就不用说了,不开启的话,nat转发就无从谈起。根据同事的业务需求来看,其应用和web之类的应用类似,只需增加一条目的地址转换即可,不 过增加后发现无效。又试着增加源地址转换,仍然无效。

二、内网虚拟机本机测试

经过上面的配置后,首先想到的内网虚拟机的iptables防火墙 。

1root@apptest[/root]/etc/init.d/iptables status
2iptables: Firewall is not running.

发现防火墙是关闭的,接着查看本机是否进行了监听,本机连接是否有效:

1root@apptest[/root]netstat -an|grep 6464
2udp        0      0 ::ffff:127.0.0.1:6464       :::*
3root@apptest[/root]nc -vuz 127.0.0.1 6464
4Connection to 127.0.0.1 6464 port [udp/*] succeeded!

经以上两步测试发现监听ok,连接也正常。排除本机自身的原因。

三、nf_conntrack表查看

接来通过nc -u发一个请求过来,查看/proc/net/nf_conntrack里的udp请求。再次验证下请求是否已经通过本机:

1cat /proc/net/nf_conntrack|grep udp
2ipv4     2 udp      17 8 src=192.168.122.31 dst=192.168.122.1 sport=40321 dport=53 src=192.168.122.1 dst=192.168.122.31 sport=53 dport=40321 mark=0 secmark=0 use=2
3ipv4     2 udp      17 29 src=61.153.111.147 dst=61.153.111.191 sport=137 dport=137 [UNREPLIED] src=61.153.111.191 dst=61.153.111.147 sport=137 dport=137 mark=0 secmark=0 use=2
4ipv4     2 udp      17 8 src=192.168.122.31 dst=192.168.122.1 sport=60904 dport=53 src=192.168.122.1 dst=192.168.122.31 sport=53 dport=60904 mark=0 secmark=0 use=2
5ipv4     2 udp      17 8 src=192.168.122.31 dst=192.168.122.1 sport=39042 dport=53 src=192.168.122.1 dst=192.168.122.31 sport=53 dport=39042 mark=0 secmark=0 use=2
6ipv4     2 udp      17 1 src=192.168.100.107 dst=192.168.100.255 sport=138 dport=138 [UNREPLIED] src=192.168.100.255 dst=192.168.100.107 sport=138 dport=138 mark=0 secmark=0 use=2

只看到以上一堆的udp端口使用信息,不过并未发现6464端口。看来该端口并未连接到物理机IP上 ,问题应该还是出在iptables本身上。通过google搜索,发现以 下两个有点久远的贴子,不过发现两个帖子最后都没有被解决:

http://bbs.csdn.net/topics/270049897

http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=2203110

四、问题解决

看来还得在iptables本身的配置上找原因,经过多次测试,发现在/etc/sysconfig/iptables的filter表中增加如下一条,并重启iptables后,问题解决:

1-A FORWARD -d 192.168.122.0/24 -i br1 -j ACCEPT

其中br1为物理主机的桥接地址 --- 和平时主机中的eth0一样,这里你可以直接就当他是eth0 。

五、总结

在查原问题的过程中,也对udp的协议也进行了一点简单学习 。其中百度百科中有关udp词条有这么一句话:UDP Helper是实现对指定UDP端口广播报文的中继转发,即将指定UDP端口的广播报文转换为单播报文发送给指定的服务器,起到中继的作用 。

看到后,就试着把上面第一部中的iptables中的dnat和snat转换给去掉,看其是否能进行广播寻址。经测试,发现去掉这两句iptables语包,竟然也可以转到内 部的虚拟机,其竟然也可以广播给目标主机, 即只用第四部就可以解决问题。

不过经细看udp的协议发现,其可以在包中包含源主机地址、源端口和目的主机地址、目的端口 的 ,而上面使用广播的方式能找到目标主机属于局域网通信中的一部分。即到达内部(即物理机和里面的N台虚拟机之间的局域网内时),没有目标地址时,其会以广播的方式通知内部所有的主机。而内部这N台机器中,监听udp 6464的又只有这一台,所以成功的接收到了回包(上面不通的原因也是回包不能通过,需增加forward规则)。而如查内部局域网环境中有多台主机同时用到6464 udp端口,刚会出现丢包的问题。所以第一部中的dnat转换还是有必要增加的。