引言
iptables 是配置 linux 网络必不可少的命令,但是网上文章都过于专注于命令细节上的操作,比如创建一个 Rule,删除一个 Chain,新手很容易看得云里雾里。要想快速理解 iptables,应该先弄懂其总体设计,之后涉及到细节上的操作,再去看文档,手册或者博客才能事半功倍,真正明白这些命令在干什么。
整体逻辑
之所以网上一些大段 iptables 操作脚本看起来复杂迷惑,就是 iptables 设计得非常灵活,不仅仅可以当防火墙,还可以进行端口转发,ip 分组过滤等等复杂的功能,甚至可以把它当成微型的编程语言,所以我们要先俯瞰 iptables 整体的操作逻辑,之后再陷入细节时才不会慌张。
Table, Chain 和 Rule
最简单的说法就是 Table 由很多个 Chain 组成,而 Chain 由一些 Rule 串起来组成, 所以称之为”链“。
Rule 就是对于请求一个断言,如果请求满足断言就执行指定的操作(官方文档中称这个操作为目标,Target),举个例子,”如果请求来自 192.168.19.123,就将其拒绝“,这就是一个 Rule,而”拒绝“就是一个目标,常见的目标有接受,拒绝,转发,调用另一个 Chain 等等。
请求会在 Chain 中自上而下遍历,直到遇到一个匹配的 Rule,然后调用它的目标。
四表五链
iptables 中 Table 的数目是规定死的四个:
- filter:过滤功能
- nat:端口映射,地址映射等
- mangle:用于对特定数据包的修改
- raw:优先级最高的 Table
还有五个预定义的 Chain:
- PREROUTING:数据包进入路由表之前
- INPUT:通过路由表后目的地为本机
- FORWARD:通过路由表后,目的地不为本机
- OUTPUT:由本机产生,向外转发
- POSTROUTIONG:发送到网卡接口之前。如下图:
路由决策是指判断数据包的目的地是否是本机,如果是则进入 INPUT Chain,否则进入 FORWARD Chain
PREROUTING ,POSTROUTIONG 和 FORWARD 只有作为路由器使用时才会被调用,正常电脑就只会经过 INPUT 和 OUTPUT
每个 Table 都含有几个预定义 Chain:
对于不同 Table 的同名 Chain,执行的优先级为(从高到低):
- raw
- mangle
- nat
- filter
将内置的所有 Chain 串起来看就象下图:
目标
比如下面的命令:
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
表示如果发现目标端口是 80 的 tcp 流量就放行,其中 -p tcp --dport 80
就是条件,而 ACCEPT
就称作目标(Target)了。
-A INPUT
表示将这条 Rule 追加到(Append)INPUT Chain 的最后面,这里没有指定 Table,默认就是 filter,也可以通过 -t
指定 Table。
常见的目标有:
- ACCEPT:接收数据包
- DROP:丢弃数据包
- REJECT:丢弃数据包并且返回一个拒绝
- REDIRECT:将数据包重定向到另一个端口
匹配规条件除了上面两个,还有一些常用的:
-s
匹配来源 ip--sport
匹配来源端口-m state
状态匹配,表示匹配数据包的状态,比如-m state --state ESTABLISHED
就表示匹配已经建立了连接的数据包
Chain 类似与函数?
其实除了内置的 Chain ,用户还可以创建自定义 Chain:
iptables -t nat -N DD
这就表示在 nat Table 中新建一条名为 DD 的 Chain。
在之前的我们画的 Chain 完整调用图中似乎没有看到自定义 Chain 的位置,那到底要怎么使用自定义 Chain 呢?
其实 Chain 与 Chain 之间是可以互相调用的。之前提到过 -j
参数用来指定目标,其实目标可以是表中的另一个 Chain,像这样:
iptables -t nat -A PREROUTING -p tcp -j DD
表示在 PREROUTING
链中的所有 tcp 协议包都会调用 DD Chain。
这里调用有可能一去不复返(比如在 DD
中遇到了 ACCEPT
, DROP
等等),也有可能像函数调用的那样 RETURN
回来,没错,有一个特殊的目标就叫做 RETURN
,用于从调用链中返回:
iptables -t nat -A DD -p tcp -j RETURN
其他常用命令
查看一个 Chain 中所有的规则:
# --line-numbers 表示要显示行号
$ iptables -t nat -L DD --line-numbers
Chain DD (1 references)
num target prot opt source destination
1 DD tcp -- anywhere anywhere
删除 Chain 中指定行号的规则(删除 DD 中行号为 1 的规则):
iptables -t nat -D DD 1
删除整个 Chain:
sudo iptables -t nat --delete-chain DD
案例:路由器的透明代理配置
为了方便,我们常会在路由器上配置透明代理,这个通过 OpenWrt 或者梅林等固件在图形界面上“点点点”就能实现,那我们为什么还要学习手动配置呢?
-
如果固件不支持你用的代理协议,比如 TROJAN
-
如果你不想用 OpenWrt 这种定制的 linux 系统,想装 Ubuntu 这样通用的 linux 系统当路由器,完全没有问题,靠一个 iptables 就可以实现 OpenWrt 的全部功能(OpenWrt 上所谓的“高级功能”,比如去广告,代理,其实就靠 iptables 实现的。。。。)
这里我们以给 TROJAN 配置透明代理为例(别的协议其实也差不多。。。),首先我们以 nat
模式启动 TROJAN 客户端(而不是我们平常用的 client
模式),nat
的配置怎么写可以看 官方配置文档。假设我们将其启动在了 1234 端口。
## 定义 TROJAN Chain
iptables -t nat -N TROJAN # 新建一个名为 TROJAN 的链
# 直连局域网地址 192.168.0.0/16
iptables -t nat -A TROJAN -d 192.168.0.0/16 -j RETURN
# 直连 SO_MARK 为 0xff 的流量(0xff 是 16 进制数,数值上等同与上面配置的 255),此规则目的是避免代理本机(网关)流量出现回环问题
iptables -t nat -A TROJAN -p tcp -j RETURN -m mark --mark 0xff
iptables -t nat -A TROJAN -p tcp -j REDIRECT --to-ports 1234 # 流量转发到 1234 端口(即 Trojan)
## 将 TROJAN Chain 添加到 PREROUTING 实现对局域网设备的透明代理
iptables -t nat -A PREROUTING -p tcp -j TROJAN
## 将 TROJAN Chain 添加到 OUTPUT 实现对本设备的透明代理
iptables -t nat -A OUTPUT -p tcp -j TROJAN
看一下 TROJAN 链的形状:
$ iptables -t nat -L TROJAN
Chain TROJAN (0 references)
target prot opt source destination
RETURN all -- anywhere 192.168.0.0/16
RETURN tcp -- anywhere anywhere mark match 0xff
REDIRECT tcp -- anywhere anywhere redir ports 1234
可以顺手看一下 OpenWrt 上启动了 ShadowSocksR Plus 插件后,它的 iptables 有什么变化(点击 Status->Firewall,展示的其实就是 iptables):
是不是感觉和我们写得差不多,除此之外,它还利用 iptables 的 ipset (就里面有很多 ip 的一个集合,可以用于判断 ip 是否在里面,这里就不详述了)功能实现到了 gfwlist:
除此之外 ShadowSocksR Plus 还实现了定时更新 gfwlist 的功能,这些功能如果我们都要自己实现的话,尽管不难,工作量还是不小的,如果只是因为协议不支持的的话,我们只要先登录 OpenWrt 命令行手动启动一个协议客户端,把 SS_SPEC_WAN_FW
链的最后一条规则改成自己的端口就可以复用 ShadowSocksR Plus 的绝大多数功能了,可见看得懂 iptables 还是有点卵用的。