最近因为有些需求,需要在路由器上设置DNAT转发。于是在网上抄了一行代码:

iptables -t nat -A PREROUTING -p tcp -m tcp --dport 8080 -j DNAT --to-destination 192.168.0.1

作为一个负责任的程序员,代码当然是抄得理直气壮。但是如果抄的代码自己没有搞懂,那就有点说不过去了。

上面这行代码,大致的含义,就是在nat表的PREROUTING链中添加一条规则。这条规则会识别出所有目标端口为8080的TCP的网络包,将目的地址改为192.168.0.1。

但从直觉(其实是其他地方抄的代码,毕竟咱抄代码也得「货比三家」不是)上来看,似乎这里的 -m tcp 可以不写。效果也是一样的。

那么问题就来了,到底写不写 -m tcp ,原因是什么?

带着这个疑问,我搜索了 iptables 的文档,发现了 -m 选项的作用是设定扩展模块。扩展模块的作用,就是扩展 iptables 在识别流量时的能力。举个简单例子:默认情况下, iptables 是不支持通过 –dport 来设置目标端口的匹配的。这个功能,或者说这个选项,是在一个叫 tcp 的扩展模块中提供的功能。关于这一部分, iptables的文档 中是这么说的:

Match Extensions

iptables can use extended packet matching modules. These are loaded in two ways: implicitly, when -p or –protocol is specified, or with the -m or –match options, followed by the matching module name; after these, various extra command line options become available, depending on the specific module. You can specify multiple extended match modules in one line, and you can use the -h or –help options after the module has been specified to receive help specific to that module.

这段文字我读了很多遍。尤其是第二句,说有两种方式( two ways )使用扩展包匹配模块。但是以我六年小学的语文断句水平,在后面的文字里,却提取出了三种(而不是两种)方式:

  • implicitly : 隐式;
  • when -p or –protocal is specified :当使用了 -p 选项时;
  • or with the -m or –match options, followed by the matching module name :当使用了 -m 选项时。

显然前后矛盾。为了搞懂到底是几种方式,以及真正的用法。我不得不脱离文档,经过多次实践验证,才搞懂了这句话的真正含义:

  • 显示指定模块 :通过 -m 模块名 选项,明确的、显示的指定要启用的扩展模块,iptables支持的模块可以参考 这个文档
  • 隐式指定模块 :当使用 -p 选项时,会默认加载对应协议命名的模块。比如说 -p tcp,除了字面表达的匹配 tcp 协议外,还隐式的表明要使用 tcp 扩展模块。换句话说, -p tcp-p tcp -m tcp是一回事(tcp以外的其他协议类似)

所以,回到开头来,代码

iptables -t nat -A PREROUTING -p tcp -m tcp --dport 8080 -j DNAT --to-destination 192.168.0.1

和代码:

iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.0.1

其实是等效的。区别是后面这个代码,隐私的启用的 tcp 这个扩展模块