分类: LINUX
2005-10-09 16:09:14
作者:shixudong@163.com 2005-01-18 16:56:31 来自:中国Linux论坛 |
iptables与socks5 |
从“iptables和natcheck”一文可知,只要在两端都采用了iptables作NAT后,即使两侧都通过了natcheck的兼容性测试,但iptables两侧永远也不能互相穿越。
怎
么办呢,一种办法是在公网上添加中转服务器,两侧内网机器之间的UDP通讯都由中转服务器来中转(其实只要中转一侧足矣)。这种方法的好处是,因为中转服
务器在公网,任何NAT后面的机器都可以和中转服务器建立连接,也就是说,不同内网之间的机器总是可以通过中转服务器实现双向通信的。然而,该办法的缺点
是,对中转服务器的要求比较高,包括CPU处理能力和网络带宽两方面,而且客户机之间的通讯延迟也是不可避免的(目前网上最为盛行的Skype
是个例外,他采用了分布式中转技术,直接挂在互联网上不在防火墙后面的Skype客户端都可以为他人提供中转服务,因此Skype在提供很高呼叫成功率的
同时还能确保超高质量的语音效果)。还有一个更为重要的因素,即中转服务器的标准不统一,导致每种不同类型的P2P程序都需要一个专用的中转服务器。倘若
这些中转服务器之间不能做到资源共享的话,必然存在资源浪费现象(标准的中转协议好像正在推出,名称为Traversal Using Relay
NAT 即TURN)。
另一种比较好的办法就是采用Socks5(Rfc1928)代理服务器取代专用的中转服务器,一是因为Socks5能
够很好的支持UDP,二是
Socks5代理服务器的品种以及在公网上部署的数量都比较多,而且最重要的是Socks5是一个已经标准化了的协议。客户端采用Socks5代理后,其
UDP通信通过Socks5中转出去,在对方的P2P程序看来,使用Socks5代理后的客户就像直接连在公网上,也就是说,只要有一方使用Socks5
代理,则另一方不论采用何种NAT,都不会受Stun或natcheck的限制。因此,iptables和Socks5理论上应该合作愉快,但在实现
Socks5代理时,如果对Socks5协议理解不够透彻,在和iptables合作时,还是有一些不愉快的。下文试举两例说明之。
假设一端
采用iptables,另一端采用Socks5,现在Socks5后面的QQ要向iptables后面的在线(不隐身)QQ发送消息。当QQ通过
Socks5代理登陆QQ服务器时,首先要和Socks5服务器建立一个基于TCP连接的Socks5通道,用于控制QQ和Socks5服务器之间的后续
UDP联结。一旦该通道成功建立,Socks5服务器将动态分配一个UDP端口为该QQ担任UDP中转的任务,当QQ给远端iptables后面在线QQ
发送消息时,先将消息(UDP包)发送到Socks5服务器上先前分配的UDP端口,再由服务器将该UDP包转发到远端QQ。当UDP包到达远端QQ时,
由iptables端口受限的属性可知,除非使用Socks5代理的QQ在线且在不久前(三分钟)曾经收到过iptables后面QQ主动发来的UDP
包,否则这个包无法被转发到iptables后面的QQ上。
那么这个UDP包最终又往哪里去了呢?我们来分析一下,该UDP包到达
iptables所在那台Linux主机后的前进流程。当一个UDP包到达Linux时,先交由iptables处理,iptables则先看该UDP包
在/proc/net/ip_conntrack文件里有无匹配项,如有,则进行匹配处理;如无,再看PREROUTING链里有无匹配规则,如有,则继
续进行匹配处理;如无,由于该包目的地址就是Linux本身,所以
iptables将其放入INPUT链。由于INPUT链的默认策略为ACCEPT,则该UDP包将被转交给Linux的本地进程处理,但事实上由于不存
在这样的本地进程,结果是Linux向产生该UDP包的机器发回一个icmp-port-unreachable包(注意,这个包是Linux系统产生
的,不是iptables的REJECT目标产生的)。如果INPUT链的策略为DROP,则该UDP包就被DROP。
由上分析可知,一般情
况下,最终iptables所在机器将向发送QQ方返回一个icmp-port-unreachable包。当
Socks5服务器收到这个icmp-port-unreachable包后,不同的Socks5服务器有不同的处理方式。一些Socks5服务器(如
Ccproxy带的Socks5)简单的忽略这个icmp-port-unreachable包,而QQ则由于没有发送成功(没有收到应答信息),继续重
复上述过程若干次,但都无法发送成功,最后只能通过服务器中转,虽然由于中转造成发送速度很慢,但总是能够成功发送。而另外一些Socks5服务器(如
Wingate带的Socks5)收到远端返回的icmp-port-unreachable包后,认为先前QQ发起的UDP联结无效,通过先前建立的
Socks5通道(TCP)给客户端返回一个general SOCKS server failure(Reply
Code‘01’),随后立即关闭该通道,同时还关闭了Socks5服务器上担任中转任务的UDP端口。然而QQ似乎并不知道它和Socks5服务器之间
的联系已被切断,由于没有发送成功(没有收到应答信息),仍然向Socks5服务器上已关闭的UDP端口发送消息。此时由于Socks5服务器已关闭了相
应的UDP端口,所以也向QQ返回一个icmp-port-unreachable包。QQ收到这个包后,继续重复上述过程若干次,最终因超时而失败,并
且因为同样的原因也不能通过服务器中转。更为严重的是,即便到了此时,QQ仍然不知道它和Socks5服务器之间的联系已被切断,因此,即使当QQ向其他
离线或隐身QQ或不在NAT后面的在线QQ发送消息时,也不能成功。
上面这个例子表明,由于某些Socks5服务器和Socks5客户端对Socks5协议理解不够透彻,即使采用了Socks5,在和iptables互通时也会导致通讯不畅。
另一个例子,考虑这样一种情形,Socks5服务器位于公网,内网客户端先通过iptables进行NAT,然后再去连接Socks5服务器。
先引用Socks5协议(Rfc1928)里如下一段文字:
The
UDP ASSOCIATE request is used to establish an association within the
UDP relay process to handle UDP datagrams. The DST.ADDR and DST.PORT
fields contain the address and port that the client expects to use to
send UDP datagrams on for the association. The server MAY use this
information to limit access to the association. If the client is not in
possesion of the information at the time of the UDP ASSOCIATE, the
client MUST use a port number and address of all zeros.
Socks5客户端在进
行UDP
ASSOCIATE时(通过TCP通道),要求用自己将来发送UDP包的地址和端口填充DST.ADDR和DST.PORT字段,以便Socks5服务器
确保其分配的UDP端口仅为对应的Socks5客户端提供中转服务。然而,当Socks5客户端跨越NAT去连接Socks5服务器时,考虑到要经过
NAT地址转换环节,Socks5客户端无法预知将来Socks5服务器看到的自己用来发送UDP包的地址和端口。针对这种情况,Rfc1928建议
Socks5客户端使用0填充DST.ADDR和DST.PORT字段,这样一来,Socks5服务器就不会限制Socks5客户端对中转端口的使用了。
然而,目前好多Socks5客户端似乎没有意识到这一点,像最通用的NEC公司的e-Border
Driver,便使用了客户端的真实IP地址和端口填充DST.ADDR和DST.PORT字段;而其他一些P2P程序自带的Socks5客户端,仅用0
填充DST.ADDR字段。鉴于跨越NAT后,IP地址必然发生变化,导致e-Border
Driver永远不能用于跨越NAT的场合,而像iptables,由于具有Preserves port
number的属性,所以,那些仅用0填充DST.ADDR字段的Socks5客户端,可以用于跨越iptables的场合。此外,跨越NAT还需要
Socks5服务器的配合,遗憾的是,仍然有些Socks5服务器并没有充分理解Rfc1928的这一建议。在这点上,Wingate带的Socks5做
得比较好,支持客户端用0填充DST.ADDR和DST.PORT字段;而Ccproxy带的Socks5,在客户端进行UDP
ASSOCIATE时,虽然也支持客户端用0填充DST.ADDR和DST.PORT字段,并能在服务器上顺利为客户端分配一个UDP端口,但当后来该
UDP端口和客户端通信时,不是发往客户端对应的UDP端口,而是发往客户端前述用0填充的DST.PORT,导致无法实现中转任务。至于NEC公司提供
的广为流传的免费Socks5服务器,则干脆申明“if the destination port is 0, we will assume
the same port as tcp's”,也将导致Socks5客户端无法跨越NAT。
在这个例子里,iptables对
Socks5代理没有任何影响,纯粹是因为Socks5本身的原因导致通讯不畅。相反,利用iptables
的保护源端口特性,还能确保部分Socks5客户端能够顺利跨越用iptables实现的NAT去连接公网上的Socks5服务器。
希望本文两个例子能对即将或已经实现Socks5代理的开发者有所帮助。