IP信息包过滤系统-iptables


iptables 是与 Linux 内核集成的 IP 信息包过滤系统。如果 Linux 系统连接到因特网或局域网、服务器或连接 LAN 和因特网的代理服务器,则该系统有利于在 Linux 系统上更好地控制 IP 信息包过滤和防火墙配置。
netfilter/iptables 信息包过滤系统就是这种功能强大的工具,可用于添加、编辑和除去规则,这些规则是在做信息包过滤决定时,防火墙所遵循和组成的规则。这些规则存储在专用的信息包过滤表中,而这些表集成在 Linux 内核中。在信息包过滤表中,规则被分组放在所谓的链(chain)或表(table)中。
虽然 netfilter/iptables 信息包过滤系统被称为单个实体,但它实际上由两个组件:netfilter 和 iptables 组成。netfilter 组件也称为内核空间(kernelspace),是内核的一部分,由一些信息包过滤表组成,这些表包含内核用来控制信息包过滤处理的规则集;iptables 组件是一种用户空间(userspace)工具,它使插入、修改和除去信息包过滤表中的规则变得容易。

iptables is the userspace command line program used to configure the Linux 2.4.x and 2.6.x IPv4 packet filtering ruleset. It is targeted towards system administrators.
Since Network Address Translation is also configured from the packet filter ruleset, iptables is used for this, too. The iptables package also includes ip6tables. ip6tables is used for configuring the IPv6 packet filter.
Features
listing the contents of the packet filter ruleset
adding/removing/modifying rules in the packet filter ruleset
listing/zeroing per-rule counters of the packet filter ruleset
Netfilter子系统包含数据包选择、过滤、修改,连接跟踪,网络地址转换(NAT)等内容。
Netfilter挂载点
在IPv4和IPv6的接收和发送路径中,看到过这些挂载点:
NF_INET_PRE_ROUTING:在IPv4中,这个挂载点位于方法ip_rcv()中。这是所有入站数据包遇到的第一个挂载点,它处在路由选择之前。
NF_INET_LOCAL_IN:在IPv4中,这个挂载点位于方法ip_local_deliver中。对于所有发给当前主机的入站数据包,经过挂载点NF_INET_PRE_ROUTING和路由选择子系统之后,都将到达这个挂载点。
NF_INET_FORWARD:在IPv4中,这个挂载点位于方法ip_forward()中。对于所有要转发的数据包,经过挂载点NF_INET_PRE_ROUTING和路由选择子系统之后,都将到达这个挂载点。
NF_INET_POST_ROUTING:在IPv4中,这个挂载点位于方法ip_output()中。所有要转发的数据包,都在经过挂载点NF_INET_FORWARD后到达这个挂载点。另外,当前主机生成的数据包经过挂载点NF_INET_LOCAL_OUT后将到达这个挂载点。
NF_INET_LOCAL_OUT:在IPv4中,这个挂载点位于方法__ip_local_out中。当前主机生成的所有出站数据包都在经过路由查找和此挂载点之后,到达挂载点NF_INET_POST_ROUTING。
Netfilter钩子回调函数返回值必须是下述五个值之一,这些值被称为netfilter verdicts(netfilter判决):
NF_DROP:默默丢弃数据包
NF_ACCEPT:数据包继续在内核协议栈中传输
NF_STOLEN:数据包不继续传输,由钩子方法进行处理
NF_QUEUE:将数据包排序,供用户空间使用
NF_REPEAT:再次调用钩子函数
注册Netfilter钩子回调函数
该方法有两个:nf_register_net_hook和nf_register_net_hooks。v4.13之前的内核版本还有两个注册接口nf_register_hook和nf_register_hooks,从v4.13版本开始内核删除了这两个接口。现代网络中,仅根据L4和L3报头来过滤流量还不够,还应考虑基于会话对包进行处理。连接跟踪能够让内核跟踪会话,连接跟踪的主要目标是为NAT打下基础。
连接跟踪初始化
下图展示了IPv4连接跟踪钩子函数在IPv4收发流程中的位置,其中绿色方块是netfilter的5个钩子挂载点,蓝色方块是连接跟踪模块注册的钩子函数:

iptables由内核部分和用户空间部分组成,核心是内核部分。其字面意思就是ip表项,每个表由struct xt_table表示。
NAT
NAT(Network Address Translation)网络地址转换,主要用于IP地址转换或端口转换。 NAT最常见的用途之一是,让局域网中一组使用私有IP地址的主机能够通过网关的公网IP访问Internet。
NAT初始化
与上节介绍的过滤表一样,NAT表也是一个xt_table对象。NAT的核心实现位于net/netfilter/nf_nat_core.c。NAT实现的基本元素为结构nf_nat_l4proto和nf_nat_l3proto。 (在v3.7之前的内核中,使用的是结构nf_nat_protocol)。这两个结构都包含函数指针manip_pkt(),它会修改数据报头。
netfilter框架
通过上文可知 netfilter 提供了 5 个 hook 点,包经过协议栈时会触发内核模块注册在这里的处理函数。其中主要包括:
NF_IP_PRE_ROUTING: 接收到的包进入协议栈后立即触发此 hook,在进行任何路由判断 (将包发往哪里)之前
NF_IP_LOCAL_IN: 接收到的包经过路由判断,如果目的是本机,将触发此 hook
NF_IP_FORWARD: 接收到的包经过路由判断,如果目的是其他机器,将触发此 hook
NF_IP_LOCAL_OUT: 本机产生的准备发送的包,在进入协议栈后立即触发此 hook
NF_IP_POST_ROUTING: 本机产生的准备发送的包或者转发的包,在经过路由判断之后, 将触发此 hook

iptables 使用 table 来组织规则,根据用来做什么类型的判断,将规则分为不同table。在每个 table 内部,规则被进一步组织成 chain,内置的 chain 是由内置的 hook 触发 的。chain 基本上能决定规则何时被匹配。内置的 chain 名字和 netfilter hook 名字是一一对应的:如PREROUTING是由 NF_IP_PRE_ROUTING hook 触发的chain。
因此不同 table 的 chain 最终都是注册到netfilter hook 。例如有三个 table 有 PRETOUTING chain,当这些 chain 注册到对应的 NF_IP_PRE_ROUTING hook 点时,它们需要指定优先级,应该依次调用哪个 table 的 PRETOUTING chain,优先级从高到低。
table种类
filter table:
这是最常用的 table 之一,用于判断是否允许一个包通过。
nat table:
其用于实现网络地址转换规则。当包进入协议栈的时候,这些规则决定是否以及如何修改包的源/目的地址,以改变包被路由时的行为。nat-table 通常用于将包路由到无法直接访问的网络。
mangle table:
用于修改包的 IP 头。例如,可以修改包的 TTL,增加或减少包可以经过的跳数。这个 table 还可以对包打只在内核内有效的“标记”,后续的 table 或工具处理的时候可以用到这些标记。标记不会修改包本身,只是在包的内核表示上做标记。
raw table:
它定义目的是使一个让包绕过连接跟踪。建立在 netfilter 之上的连接跟踪特性使得 iptables 将包看作已有的连接或会话的一部分,而不是一个由独立、不相关的包组成的流。数据包到达网络接口之后很快就会有连接跟踪逻辑判断。
security table:
作用是给包打上 SELinux 标记,以此影响 SELinux 或其他可以解读 SELinux 安全上下文的系统处理包的行为。这些标记可以基于单个包,也可以基于连接。
每种 table 实现的 chain

当一个包触发 netfilter hook 时,处理过程将沿着列从上向下执行。其有内置的优先级,数值越小越优先。
enum nf_ip_hook_priorities {
NF_IP_PRI_FIRST = INT_MIN,
NF_IP_PRI_RAW_BEFORE_DEFRAG = -450,
NF_IP_PRI_CONNTRACK_DEFRAG = -400,
NF_IP_PRI_RAW = -300, //raw
NF_IP_PRI_SELINUX_FIRST = -225,
NF_IP_PRI_CONNTRACK = -200, //conntrack
NF_IP_PRI_MANGLE = -150, //manage
NF_IP_PRI_NAT_DST = -100, //dnat
NF_IP_PRI_FILTER = 0, //filter
NF_IP_PRI_SECURITY = 50, //security
NF_IP_PRI_NAT_SRC = 100, //snat
NF_IP_PRI_SELINUX_LAST = 225,
NF_IP_PRI_CONNTRACK_HELPER = 300,
NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,
NF_IP_PRI_LAST = INT_MAX,
};
特定事件会导致 table 的 chain 被跳过。例如,只有每个连接的第一个包会去匹配 NAT 规则,对这个包的动作会应用于此连接后面的所有包。到这个连接的应答包会被自动应用反方向的 NAT 规则。
对于不同的包,由于netfilter挂载函数的不同,导致其对应的chain也不同:
收到的、目的是本机的包:PRETOUTING -> INPUT
收到的、目的是其他主机的包:PRETOUTING -> FORWARD -> POSTROUTING
本地产生的包:OUTPUT -> POSTROUTING
综合前面讨论的 table 顺序问题,我们可以看到对于一个收到的、目的是本机的包: 首先依次经过 PRETOUTING chain 上面的 raw、mangle、nat table;然后依次经 过 INPUT chain 的 mangle、filter、security、nat table,然后才会到达本机的某个 socket。

iptables 规则
规则放置在特定 table 的特定 chain 里面。当 chain 被调用的时候,包会依次匹配其里面的规则。每条规则都有一个匹配部分和一个目标部分。规则的匹配部分指定了一些条件,包必须满足这些条件才会和相应的将要执行的动作进行关联。规则可以匹配协议类型、目的或源地址、目的或源端口、目的或源网段、接收或发送的接口(网卡)、协议头、连接状态等等条件。这些综合起来,能够组合成非常复杂的规则来区分不同的网络流量。
包符合某种规则的条件而触发的动作叫做目标。目标分为两种类型:
终止目标:这种 target 会终止 chain 的匹配,将控制权转移回 netfilter hook。根据返回值的不同,hook 或者将包丢弃,或者允许包进行下一 阶段的处理。
非终止目标:非终止目标执行动作,然后继续 chain 的执行。虽然每个 chain 最终都会回到一个终止目标,但是在这之前,可以执行任意多个非终止目标。
每个规则可以跳转到哪个 target 依上下文而定,例如,table 和 chain 可能会设置 target 可用或不可用。规则里激活的 extensions 和匹配条件也影响 target 的可用性。
还有一种特殊的非终止目标:跳转目标。jump target 是跳转到其他 chain 继续处理的动作。向用户自定义 chain 添加规则和向内置的 chain 添加规则的方式是相同的。不同的地方在于, 用户定义的 chain 只能通过从另一个规则跳转(jump)到它,因为它们没有注册到 netfilter hook。用户定义的 chain 可以看作是对调用它的 chain 的扩展。
iptables 和 conntrack
在讨论 raw table 和 匹配连接状态的时候,介绍了构建在 netfilter 之上的连接跟踪系统。连接跟踪系统使得 iptables 基于连接上下文而不是单个包来做出规则判断, 给 iptables 提供了有状态操作的功能。
跟踪系统将包和已有的连接进行比较,如果包所属的连接已经存在就更新连接状态, 否则就创建一个新连接。如果 raw table 的某个 chain 对包标记为目标是 NOTRACK, 那这个包会跳过连接跟踪系统。
连接跟踪系统中的连接状态有:
NEW:如果到达的包关连不到任何已有的连接,但包是合法的,就为这个包创建一个新连接。对 面向连接的(connection-aware)的协议例如 TCP 以及非面向连接的(connectionless )的协议例如 UDP 都适用。
ESTABLISHED:当一个连接收到应答方向的合法包时,状态从 NEW 变成 ESTABLISHED。对 TCP 这个合法包其实就是 SYN/ACK 包;对 UDP 和 ICMP 是源和目 的 IP 与原包相反的包。
RELATED:包不属于已有的连接,但是和已有的连接有一定关系。这可能是辅助连接( helper connection),例如 FTP 数据传输连接,或者是其他协议试图建立连接时的 ICMP 应答包。
INVALID:包不属于已有连接,并且因为某些原因不能用来创建一个新连接,例如无法 识别、无法路由等等。
UNTRACKED:如果在 raw table 中标记为目标是 UNTRACKED,这个包将不会进入连 接跟踪系统。
SNAT:包的源地址被 NAT 修改之后会进入的虚拟状态。连接跟踪系统据此在收到反向包时对地址做反向转换。
DNAT:包的目的地址被 NAT 修改之后会进入的虚拟状态。连接跟踪系统据此在收到反向包时对地址做反向转换。
iptable使用
iptables可以配置和管理 Netfilter 框架,其是一个用户态工具,用以定义网络数据包过滤规则,此处主要以NAT为例作为讲解。
NAT
NAT是一种网络技术,用于在网络设备(如路由器、网关)上对数据包的源地址或目的地址进行修改,通常在局域网与互联网之间进行转换。NAT 主要用于节约 IPv4 地址,隐藏内部网络的结构,并在网络层提供一定程度的安全性。其在路由器或防火墙处修改通过的数据包的 IP 地址信息。常见的 NAT 类型包括:
1.SNAT(Source NAT):修改数据包的源 IP 地址,通常用于内网设备访问外网时,将内网设备的私有 IP 地址替换为公共 IP 地址。
2.DNAT(Destination NAT):修改数据包的目的 IP 地址,通常用于将外部请求映射到内网的某台服务器(如端口转发)。
NAT通过维护一个映射表,跟踪修改前后的 IP 地址和端口号,从而保证数据包能正确地返回给发起请求的内部设备。
NAT类型
1.静态 NAT:一个内部 IP 地址映射到一个固定的外部 IP 地址。通常用于让内部服务器暴露给外部网络。
2.动态 NAT:内部 IP 地址池映射到外部 IP 地址池。适用于内网设备临时需要与外网通信,使用的外部 IP 地址动态分配。
3.PAT(端口地址转换),也叫端口多路复用:多个内部 IP 地址可以共享一个或多个外部 IP 地址,但通过不同的端口区分不同的连接。它是 NAT 中最常见的类型,也称为NAT 重载。
和 NAT 相关的最重要的规则,都在 nat-table 里。在相应chain中配置所需的规则,即可实现NAT的功能。
iptables可以配置和管理 Netfilter 框架,定义网络数据包过滤规则:
iptables [-t table] command [match pattern] [action]
在指定好table和chain之后,需要对匹配模式进行指定:
iptables -t nat -A POSTROUTING -p tcp -s 192.168.1.2 [...]
iptables -t nat -A POSTROUTING -p udp -d 192.168.1.2 [...]
iptables -t nat -A PREROUTING -s 192.168.0.0/16 -i eth0 [...]
至此,已经可以指定匹配模式来过滤包了,接下来就是选择合适的动作,对于 nat table,有如下几种动作SNAT, MASQUERADE, DNAT, REDIRECT,都需要通过 -j 指定。
iptables [...] -j SNAT --to-source 123.123.123.123
iptables [...] -j MASQUERADE
iptables [...] -j DNAT --to-destination 123.123.123.123:22
iptables [...] -j REDIRECT --to-ports 8080
SNAT:修改源 IP 为固定新 IP,SNAT 只对离开路由器的包有意义,因此它只用在 POSTROUTING chain 中
MASQUERADE:修改源 IP 为动态新 IP,和 SNAT 类似,但是对每个包都会动态获取指定输出接口(网卡)的 IP
DNAT: 修改目的 IP
REDIRECT: 将包重定向到本机另一个端口
希望实现的是:从本地网络发出的、目的是公网的包,将发送方地址修改为路由器的地址。接下来假设路由器的本地网络走 eth0 端口,到公网的网络走 eth1 端口。那么如下iptables命令就能完成期望的功能:
iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE
iptables调用逻辑
iptables的具体实现逻辑如下图所示

在 iptables 中,从用户态添加一个规则到内核态的流程如下:
用户态调用:
1.用户通过 iptables 命令行工具调用添加规则的命令;
2.该命令被解析,并调用相应的库函数(通常是 libiptc 中的函数)。
准备规则:
在用户空间,命令行工具会构造规则结构体,并将规则信息填充到这个结构体中。
调用 libiptc:
1.用户态工具调用 libiptc 中的函数,例如 iptc_append_entry,来将规则添加到指定的链中。
2.libiptc 会使用套接字(通常是原始套接字)与内核通信。
套接字通信:
1.libiptc 使用 socket 系统调用创建一个原始套接字,并使用 getsockopt 或 setsockopt 来发送规则。
2.具体而言,会调用 TC_INIT 来初始化句柄,并通过 getsockopt 获取当前规则的信息。
进入内核态:
当调用 setsockopt 发送规则时,控制权转移到内核态。内核中的 netfilter 处理逻辑会处理这个请求。
处理规则:
1.内核中的 netfilter 接收这个请求,并将规则添加到相应的链中。具体实现位于 net/netfilter 目录下。
2.内核会更新相应的数据结构(如 ip_tables)。
返回结果:
添加规则后,内核会将结果返回给用户态程序,通常通过返回值或设置 errno 的方式。
用户态接收结果:
用户态工具接收内核的返回结果,并打印成功或错误信息给用户。
最新版本:1.4
该版本支持新的 cttimeout 架构,允许你通过 iptables 的 CT 目标来附加指定的超时策略。
项目主页:http://www.netfilter.org/projects/iptables/
netfilter/iptables 信息包过滤系统就是这种功能强大的工具,可用于添加、编辑和除去规则,这些规则是在做信息包过滤决定时,防火墙所遵循和组成的规则。这些规则存储在专用的信息包过滤表中,而这些表集成在 Linux 内核中。在信息包过滤表中,规则被分组放在所谓的链(chain)或表(table)中。
虽然 netfilter/iptables 信息包过滤系统被称为单个实体,但它实际上由两个组件:netfilter 和 iptables 组成。netfilter 组件也称为内核空间(kernelspace),是内核的一部分,由一些信息包过滤表组成,这些表包含内核用来控制信息包过滤处理的规则集;iptables 组件是一种用户空间(userspace)工具,它使插入、修改和除去信息包过滤表中的规则变得容易。

iptables is the userspace command line program used to configure the Linux 2.4.x and 2.6.x IPv4 packet filtering ruleset. It is targeted towards system administrators.
Since Network Address Translation is also configured from the packet filter ruleset, iptables is used for this, too. The iptables package also includes ip6tables. ip6tables is used for configuring the IPv6 packet filter.
Features
listing the contents of the packet filter ruleset
adding/removing/modifying rules in the packet filter ruleset
listing/zeroing per-rule counters of the packet filter ruleset
Netfilter子系统包含数据包选择、过滤、修改,连接跟踪,网络地址转换(NAT)等内容。
Netfilter挂载点
在IPv4和IPv6的接收和发送路径中,看到过这些挂载点:
NF_INET_PRE_ROUTING:在IPv4中,这个挂载点位于方法ip_rcv()中。这是所有入站数据包遇到的第一个挂载点,它处在路由选择之前。
NF_INET_LOCAL_IN:在IPv4中,这个挂载点位于方法ip_local_deliver中。对于所有发给当前主机的入站数据包,经过挂载点NF_INET_PRE_ROUTING和路由选择子系统之后,都将到达这个挂载点。
NF_INET_FORWARD:在IPv4中,这个挂载点位于方法ip_forward()中。对于所有要转发的数据包,经过挂载点NF_INET_PRE_ROUTING和路由选择子系统之后,都将到达这个挂载点。
NF_INET_POST_ROUTING:在IPv4中,这个挂载点位于方法ip_output()中。所有要转发的数据包,都在经过挂载点NF_INET_FORWARD后到达这个挂载点。另外,当前主机生成的数据包经过挂载点NF_INET_LOCAL_OUT后将到达这个挂载点。
NF_INET_LOCAL_OUT:在IPv4中,这个挂载点位于方法__ip_local_out中。当前主机生成的所有出站数据包都在经过路由查找和此挂载点之后,到达挂载点NF_INET_POST_ROUTING。
Netfilter钩子回调函数返回值必须是下述五个值之一,这些值被称为netfilter verdicts(netfilter判决):
NF_DROP:默默丢弃数据包
NF_ACCEPT:数据包继续在内核协议栈中传输
NF_STOLEN:数据包不继续传输,由钩子方法进行处理
NF_QUEUE:将数据包排序,供用户空间使用
NF_REPEAT:再次调用钩子函数
注册Netfilter钩子回调函数
该方法有两个:nf_register_net_hook和nf_register_net_hooks。v4.13之前的内核版本还有两个注册接口nf_register_hook和nf_register_hooks,从v4.13版本开始内核删除了这两个接口。现代网络中,仅根据L4和L3报头来过滤流量还不够,还应考虑基于会话对包进行处理。连接跟踪能够让内核跟踪会话,连接跟踪的主要目标是为NAT打下基础。
连接跟踪初始化
下图展示了IPv4连接跟踪钩子函数在IPv4收发流程中的位置,其中绿色方块是netfilter的5个钩子挂载点,蓝色方块是连接跟踪模块注册的钩子函数:

iptables由内核部分和用户空间部分组成,核心是内核部分。其字面意思就是ip表项,每个表由struct xt_table表示。
NAT
NAT(Network Address Translation)网络地址转换,主要用于IP地址转换或端口转换。 NAT最常见的用途之一是,让局域网中一组使用私有IP地址的主机能够通过网关的公网IP访问Internet。
NAT初始化
与上节介绍的过滤表一样,NAT表也是一个xt_table对象。NAT的核心实现位于net/netfilter/nf_nat_core.c。NAT实现的基本元素为结构nf_nat_l4proto和nf_nat_l3proto。 (在v3.7之前的内核中,使用的是结构nf_nat_protocol)。这两个结构都包含函数指针manip_pkt(),它会修改数据报头。
netfilter框架
通过上文可知 netfilter 提供了 5 个 hook 点,包经过协议栈时会触发内核模块注册在这里的处理函数。其中主要包括:
NF_IP_PRE_ROUTING: 接收到的包进入协议栈后立即触发此 hook,在进行任何路由判断 (将包发往哪里)之前
NF_IP_LOCAL_IN: 接收到的包经过路由判断,如果目的是本机,将触发此 hook
NF_IP_FORWARD: 接收到的包经过路由判断,如果目的是其他机器,将触发此 hook
NF_IP_LOCAL_OUT: 本机产生的准备发送的包,在进入协议栈后立即触发此 hook
NF_IP_POST_ROUTING: 本机产生的准备发送的包或者转发的包,在经过路由判断之后, 将触发此 hook

iptables 使用 table 来组织规则,根据用来做什么类型的判断,将规则分为不同table。在每个 table 内部,规则被进一步组织成 chain,内置的 chain 是由内置的 hook 触发 的。chain 基本上能决定规则何时被匹配。内置的 chain 名字和 netfilter hook 名字是一一对应的:如PREROUTING是由 NF_IP_PRE_ROUTING hook 触发的chain。
因此不同 table 的 chain 最终都是注册到netfilter hook 。例如有三个 table 有 PRETOUTING chain,当这些 chain 注册到对应的 NF_IP_PRE_ROUTING hook 点时,它们需要指定优先级,应该依次调用哪个 table 的 PRETOUTING chain,优先级从高到低。
table种类
filter table:
这是最常用的 table 之一,用于判断是否允许一个包通过。
nat table:
其用于实现网络地址转换规则。当包进入协议栈的时候,这些规则决定是否以及如何修改包的源/目的地址,以改变包被路由时的行为。nat-table 通常用于将包路由到无法直接访问的网络。
mangle table:
用于修改包的 IP 头。例如,可以修改包的 TTL,增加或减少包可以经过的跳数。这个 table 还可以对包打只在内核内有效的“标记”,后续的 table 或工具处理的时候可以用到这些标记。标记不会修改包本身,只是在包的内核表示上做标记。
raw table:
它定义目的是使一个让包绕过连接跟踪。建立在 netfilter 之上的连接跟踪特性使得 iptables 将包看作已有的连接或会话的一部分,而不是一个由独立、不相关的包组成的流。数据包到达网络接口之后很快就会有连接跟踪逻辑判断。
security table:
作用是给包打上 SELinux 标记,以此影响 SELinux 或其他可以解读 SELinux 安全上下文的系统处理包的行为。这些标记可以基于单个包,也可以基于连接。
每种 table 实现的 chain

当一个包触发 netfilter hook 时,处理过程将沿着列从上向下执行。其有内置的优先级,数值越小越优先。
enum nf_ip_hook_priorities {
NF_IP_PRI_FIRST = INT_MIN,
NF_IP_PRI_RAW_BEFORE_DEFRAG = -450,
NF_IP_PRI_CONNTRACK_DEFRAG = -400,
NF_IP_PRI_RAW = -300, //raw
NF_IP_PRI_SELINUX_FIRST = -225,
NF_IP_PRI_CONNTRACK = -200, //conntrack
NF_IP_PRI_MANGLE = -150, //manage
NF_IP_PRI_NAT_DST = -100, //dnat
NF_IP_PRI_FILTER = 0, //filter
NF_IP_PRI_SECURITY = 50, //security
NF_IP_PRI_NAT_SRC = 100, //snat
NF_IP_PRI_SELINUX_LAST = 225,
NF_IP_PRI_CONNTRACK_HELPER = 300,
NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,
NF_IP_PRI_LAST = INT_MAX,
};
特定事件会导致 table 的 chain 被跳过。例如,只有每个连接的第一个包会去匹配 NAT 规则,对这个包的动作会应用于此连接后面的所有包。到这个连接的应答包会被自动应用反方向的 NAT 规则。
对于不同的包,由于netfilter挂载函数的不同,导致其对应的chain也不同:
收到的、目的是本机的包:PRETOUTING -> INPUT
收到的、目的是其他主机的包:PRETOUTING -> FORWARD -> POSTROUTING
本地产生的包:OUTPUT -> POSTROUTING
综合前面讨论的 table 顺序问题,我们可以看到对于一个收到的、目的是本机的包: 首先依次经过 PRETOUTING chain 上面的 raw、mangle、nat table;然后依次经 过 INPUT chain 的 mangle、filter、security、nat table,然后才会到达本机的某个 socket。

iptables 规则
规则放置在特定 table 的特定 chain 里面。当 chain 被调用的时候,包会依次匹配其里面的规则。每条规则都有一个匹配部分和一个目标部分。规则的匹配部分指定了一些条件,包必须满足这些条件才会和相应的将要执行的动作进行关联。规则可以匹配协议类型、目的或源地址、目的或源端口、目的或源网段、接收或发送的接口(网卡)、协议头、连接状态等等条件。这些综合起来,能够组合成非常复杂的规则来区分不同的网络流量。
包符合某种规则的条件而触发的动作叫做目标。目标分为两种类型:
终止目标:这种 target 会终止 chain 的匹配,将控制权转移回 netfilter hook。根据返回值的不同,hook 或者将包丢弃,或者允许包进行下一 阶段的处理。
非终止目标:非终止目标执行动作,然后继续 chain 的执行。虽然每个 chain 最终都会回到一个终止目标,但是在这之前,可以执行任意多个非终止目标。
每个规则可以跳转到哪个 target 依上下文而定,例如,table 和 chain 可能会设置 target 可用或不可用。规则里激活的 extensions 和匹配条件也影响 target 的可用性。
还有一种特殊的非终止目标:跳转目标。jump target 是跳转到其他 chain 继续处理的动作。向用户自定义 chain 添加规则和向内置的 chain 添加规则的方式是相同的。不同的地方在于, 用户定义的 chain 只能通过从另一个规则跳转(jump)到它,因为它们没有注册到 netfilter hook。用户定义的 chain 可以看作是对调用它的 chain 的扩展。
iptables 和 conntrack
在讨论 raw table 和 匹配连接状态的时候,介绍了构建在 netfilter 之上的连接跟踪系统。连接跟踪系统使得 iptables 基于连接上下文而不是单个包来做出规则判断, 给 iptables 提供了有状态操作的功能。
跟踪系统将包和已有的连接进行比较,如果包所属的连接已经存在就更新连接状态, 否则就创建一个新连接。如果 raw table 的某个 chain 对包标记为目标是 NOTRACK, 那这个包会跳过连接跟踪系统。
连接跟踪系统中的连接状态有:
NEW:如果到达的包关连不到任何已有的连接,但包是合法的,就为这个包创建一个新连接。对 面向连接的(connection-aware)的协议例如 TCP 以及非面向连接的(connectionless )的协议例如 UDP 都适用。
ESTABLISHED:当一个连接收到应答方向的合法包时,状态从 NEW 变成 ESTABLISHED。对 TCP 这个合法包其实就是 SYN/ACK 包;对 UDP 和 ICMP 是源和目 的 IP 与原包相反的包。
RELATED:包不属于已有的连接,但是和已有的连接有一定关系。这可能是辅助连接( helper connection),例如 FTP 数据传输连接,或者是其他协议试图建立连接时的 ICMP 应答包。
INVALID:包不属于已有连接,并且因为某些原因不能用来创建一个新连接,例如无法 识别、无法路由等等。
UNTRACKED:如果在 raw table 中标记为目标是 UNTRACKED,这个包将不会进入连 接跟踪系统。
SNAT:包的源地址被 NAT 修改之后会进入的虚拟状态。连接跟踪系统据此在收到反向包时对地址做反向转换。
DNAT:包的目的地址被 NAT 修改之后会进入的虚拟状态。连接跟踪系统据此在收到反向包时对地址做反向转换。
iptable使用
iptables可以配置和管理 Netfilter 框架,其是一个用户态工具,用以定义网络数据包过滤规则,此处主要以NAT为例作为讲解。
NAT
NAT是一种网络技术,用于在网络设备(如路由器、网关)上对数据包的源地址或目的地址进行修改,通常在局域网与互联网之间进行转换。NAT 主要用于节约 IPv4 地址,隐藏内部网络的结构,并在网络层提供一定程度的安全性。其在路由器或防火墙处修改通过的数据包的 IP 地址信息。常见的 NAT 类型包括:
1.SNAT(Source NAT):修改数据包的源 IP 地址,通常用于内网设备访问外网时,将内网设备的私有 IP 地址替换为公共 IP 地址。
2.DNAT(Destination NAT):修改数据包的目的 IP 地址,通常用于将外部请求映射到内网的某台服务器(如端口转发)。
NAT通过维护一个映射表,跟踪修改前后的 IP 地址和端口号,从而保证数据包能正确地返回给发起请求的内部设备。
NAT类型
1.静态 NAT:一个内部 IP 地址映射到一个固定的外部 IP 地址。通常用于让内部服务器暴露给外部网络。
2.动态 NAT:内部 IP 地址池映射到外部 IP 地址池。适用于内网设备临时需要与外网通信,使用的外部 IP 地址动态分配。
3.PAT(端口地址转换),也叫端口多路复用:多个内部 IP 地址可以共享一个或多个外部 IP 地址,但通过不同的端口区分不同的连接。它是 NAT 中最常见的类型,也称为NAT 重载。
和 NAT 相关的最重要的规则,都在 nat-table 里。在相应chain中配置所需的规则,即可实现NAT的功能。
iptables可以配置和管理 Netfilter 框架,定义网络数据包过滤规则:
iptables [-t table] command [match pattern] [action]
在指定好table和chain之后,需要对匹配模式进行指定:
iptables -t nat -A POSTROUTING -p tcp -s 192.168.1.2 [...]
iptables -t nat -A POSTROUTING -p udp -d 192.168.1.2 [...]
iptables -t nat -A PREROUTING -s 192.168.0.0/16 -i eth0 [...]
至此,已经可以指定匹配模式来过滤包了,接下来就是选择合适的动作,对于 nat table,有如下几种动作SNAT, MASQUERADE, DNAT, REDIRECT,都需要通过 -j 指定。
iptables [...] -j SNAT --to-source 123.123.123.123
iptables [...] -j MASQUERADE
iptables [...] -j DNAT --to-destination 123.123.123.123:22
iptables [...] -j REDIRECT --to-ports 8080
SNAT:修改源 IP 为固定新 IP,SNAT 只对离开路由器的包有意义,因此它只用在 POSTROUTING chain 中
MASQUERADE:修改源 IP 为动态新 IP,和 SNAT 类似,但是对每个包都会动态获取指定输出接口(网卡)的 IP
DNAT: 修改目的 IP
REDIRECT: 将包重定向到本机另一个端口
希望实现的是:从本地网络发出的、目的是公网的包,将发送方地址修改为路由器的地址。接下来假设路由器的本地网络走 eth0 端口,到公网的网络走 eth1 端口。那么如下iptables命令就能完成期望的功能:
iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE
iptables调用逻辑
iptables的具体实现逻辑如下图所示

在 iptables 中,从用户态添加一个规则到内核态的流程如下:
用户态调用:
1.用户通过 iptables 命令行工具调用添加规则的命令;
2.该命令被解析,并调用相应的库函数(通常是 libiptc 中的函数)。
准备规则:
在用户空间,命令行工具会构造规则结构体,并将规则信息填充到这个结构体中。
调用 libiptc:
1.用户态工具调用 libiptc 中的函数,例如 iptc_append_entry,来将规则添加到指定的链中。
2.libiptc 会使用套接字(通常是原始套接字)与内核通信。
套接字通信:
1.libiptc 使用 socket 系统调用创建一个原始套接字,并使用 getsockopt 或 setsockopt 来发送规则。
2.具体而言,会调用 TC_INIT 来初始化句柄,并通过 getsockopt 获取当前规则的信息。
进入内核态:
当调用 setsockopt 发送规则时,控制权转移到内核态。内核中的 netfilter 处理逻辑会处理这个请求。
处理规则:
1.内核中的 netfilter 接收这个请求,并将规则添加到相应的链中。具体实现位于 net/netfilter 目录下。
2.内核会更新相应的数据结构(如 ip_tables)。
返回结果:
添加规则后,内核会将结果返回给用户态程序,通常通过返回值或设置 errno 的方式。
用户态接收结果:
用户态工具接收内核的返回结果,并打印成功或错误信息给用户。
最新版本:1.4
该版本支持新的 cttimeout 架构,允许你通过 iptables 的 CT 目标来附加指定的超时策略。
项目主页:http://www.netfilter.org/projects/iptables/