For the complete documentation index, see llms.txt. This page is also available as Markdown.

PF NAT

简介

网络地址转换(NAT)是将整个网络(或多个网络)映射到单个 IP 地址的方法。当互联网服务提供商分配给你的 IP 地址数少于需要接入互联网的计算机总数时,就需要 NAT。NAT 在 RFC 1631 中描述。

NAT 让你能利用 RFC 1918 描述的保留地址块。通常,内部网络会使用一个或多个这样的网络块来搭建。它们是:

10.0.0.0/8       (10.0.0.0 - 10.255.255.255)
172.16.0.0/12    (172.16.0.0 - 172.31.255.255)
192.168.0.0/16   (192.168.0.0 - 192.168.255.255)

做 NAT 的 OpenBSD 系统至少会有两块网络接口,一块接互联网,另一块接内部网络。NAT 会转换来自内部网络的请求,让它们看起来都来自 OpenBSD NAT 系统本身。

NAT 工作原理

当内部网络上的客户端联系互联网上的机器时,会发出以该机器为目的地的 IP 数据包。这些数据包包含抵达目的地所需的全部寻址信息。NAT 关注这些信息:

  • 源 IP 地址(如 192.168.1.35

  • 源 TCP 或 UDP 端口(如 2132)

数据包穿越 NAT 网关时会被修改,使其看起来像是来自 NAT 网关本身。NAT 网关会将所做的修改记录在状态表中,以便 a) 在返回数据包上反向还原这些修改,b) 确保返回数据包穿过防火墙而不被阻断。例如,可能做如下修改:

  • 源 IP:替换为网关的外部地址(如 198.51.100.1

  • 源端口:替换为网关上随机选取的未用端口(如 53136)

内部机器与互联网主机都觉察不到这些转换步骤。对内部机器而言,NAT 系统只是互联网网关。对互联网主机而言,数据包看起来直接来自 NAT 系统;它完全不知道内部工作站的存在。

互联网主机回复内部机器的数据包时,会发往 NAT 网关的外部 IP(198.51.100.1)的转换端口(53136)。NAT 网关随后在状态表中查找,判断回复数据包是否匹配已建立的连接。会基于 IP/端口组合找到唯一匹配,告知 PF 这些数据包属于内部机器 192.168.1.35 发起的连接。PF 随后对返回数据包做与出站数据包相反的修改,并将其转发给内部机器。

ICMP 数据包的转换方式类似,但不修改源端口。

IP 转发

由于 NAT 几乎总用在路由器与网络网关上,可能需要启用 IP 转发,让数据包能在 OpenBSD 机器的网络接口间穿行。IP 转发通过 sysctl 机制启用:

或针对 IPv6:

配置 NAT

NAT 以可选的 nat-to 参数形式指定给出站 pass 规则。通常不直接设在 pass 规则上,而是使用 match 规则。当数据包被 match 规则选中时,该规则中的参数(如 nat-to)会被记住,并在遇到匹配该数据包的 pass 规则时应用到数据包上。这允许一整类数据包由单条 match 规则处理,是否允许流量通过的具体决定则由 blockpass 规则做出。

pf.conf 中的通用格式大致如下:

match

当数据包穿越规则集并匹配 match 规则时,该规则中指定的任何可选参数都会被记住以备后用(变为"粘性")。

pass

此规则允许数据包被传输。若数据包先前被指定了参数的 match 规则匹配过,那些参数会应用到此数据包。pass 规则可有自己的参数;这些参数优先于 match 规则中指定的参数。

out

指定此规则适用的数据包流向。nat-to 仅可对出站数据包指定。

log

通过 pflogd 记录匹配的数据包。通常仅记录首个匹配的数据包。要记录所有匹配的数据包,使用 log (all)

interface

传输数据包的网络接口名或组。

af

地址族,inet 表示 IPv4,inet6 表示 IPv6。PF 通常能根据源/目的地址推断此参数。

protocol

允许的数据包协议(如 tcp、udp、icmp)。若指定了 src_portdst_port,则必须给出协议:

src_addr

将被转换的数据包源(内部)地址。源地址可指定为:

  • 单个 IPv4 或 IPv6 地址。

  • CIDR 网络块。

  • 完整限定域名,加载规则集时通过 DNS 解析。所有解析得到的 IP 地址都会替换进规则。

  • 网络接口的名称或组。接口上的所有 IPv4 与 IPv6 地址会在加载时替换进规则。

  • 网络接口名后跟 /*netmask*(如 /24)。接口上的每个 IP 地址与网络掩码组合成 CIDR 网络块,替换进规则。

  • 网络接口名或组后跟以下任一修饰符:

    • :network — 替换为 CIDR 网络块(如 192.168.0.0/24

    • :broadcast — 替换为网络广播地址(如 192.168.0.255

    • :peer — 替换为点对点链路上对端的 IP 地址

      此外,:0 修饰符可附加在接口名/组或上述任一修饰符之后,表示 PF 不应将别名 IP 地址纳入替换。接口放在括号中时也可使用这些修饰符。例:fxp0:network:0

  • 上述任一项前加 !("非")修饰符取反。

  • 使用列表指定的一组地址。

  • 关键字 any,表示所有地址

src_port

第四层包头中的源端口。端口可指定为:

  • 1 到 65535 之间的数字

  • services(5) 中的有效服务名

  • 使用列表指定的一组端口

  • 范围:

    • !=(不等于)

    • <(小于)

    • >(大于)

    • <=(小于或等于)

    • >=(大于或等于)

    • ><(范围)

    • <>(反向范围)

      最后两个是二元运算符(接受两个参数),且不包含参数本身。

    • :(闭区间)

      闭区间运算符也是二元运算符,且包含参数本身。

port 选项在 nat 规则中通常不用,因为目的通常是无论端口如何都对所有流量做 NAT。

dst_addr

将被转换的数据包目的地址。目的地址的指定方式与源地址相同。

dst_port

第四层包头中的目的端口。此端口的指定方式与源端口相同。

ext_addr

NAT 网关上数据包将被转换到的外部(转换)地址。外部地址可指定为:

  • 单个 IPv4 或 IPv6 地址。

  • CIDR 网络块。

  • 完整限定域名,加载规则集时通过 DNS 解析。

  • 外部网络接口的名称或组。接口上的所有 IP 地址会在加载时替换进规则。

  • 外部网络接口名或组放在括号 ( ) 中。这告诉 PF:当命名接口上的 IP 地址变化时更新规则。这对通过 DHCP 或拨号获取 IP 地址的外部接口极为有用,无需每次地址变化都重载规则集。

  • 网络接口名或组后跟以下任一修饰符:

    • :network — 替换为 CIDR 网络块(如 192.168.0.0/24

    • :peer — 替换为点对点链路上对端的 IP 地址

      此外,:0 修饰符可附加在接口名/组或上述任一修饰符之后,表示 PF 不应将别名 IP 地址纳入替换。接口放在括号中时也可使用这些修饰符。例:fxp0:network:0

  • 使用列表指定的一组地址。

pool_type

指定用于转换的地址池类型。

static-port

告诉 PF 不要转换 TCP 与 UDP 数据包中的源端口。

这些行的最基本形式可能如下:

或者直接使用:

此规则表示对来自 192.168.1.0/24、穿越 tl0 接口的所有数据包执行 NAT,将源 IP 地址替换为 198.51.100.1

上面的规则虽然正确,但并非推荐形式。维护可能困难,因为外部或内部网络号任何变化都需要修改此行。与之对照,下面这行更易维护(tl0 为外部,dc0 为内部):

优点相当明显:可任改两块接口的 IP 地址而无需修改此规则。注意,此情形下应指定 inet 以确保仅使用 IPv4 地址,避免意外。

如上以接口名指定转换地址时,IP 地址在 pf.conf 加载时确定,而非动态确定。若用 DHCP 配置外部接口,这可能成为问题。若所分配的 IP 地址变化,NAT 会继续用旧 IP 地址转换出站数据包,导致出站连接停止工作。要规避此问题,可将接口名放在括号中,让 PF 自动更新转换地址:

此方法对 IPv4 与 IPv6 地址的转换都适用。

双向映射(1:1 映射)

使用 binat-to 参数可建立双向映射。binat-to 规则在内部 IP 地址与外部地址间建立一对一映射。这很有用,例如可为内部网络中的 Web 服务器提供自己的外部 IP 地址。从互联网到外部地址的连接会被转换到内部地址,而从 Web 服务器发出的连接(如 DNS 请求)会被转换到外部地址。binat-to 规则不会像 nat 规则那样修改 TCP 与 UDP 端口。

示例:

转换规则例外

若需转换大多数流量,但在某些情况下需要例外,请确保例外由不含 nat-to 参数的过滤规则处理。例如,若上面的 NAT 示例修改为:

那么整个 192.168.1.0/24 网络的数据包都会被转换到外部地址 198.51.100.79192.168.1.208 除外。

检查 NAT 状态

要查看活动的 NAT 转换,使用 pfctl 的 -s state 选项。此选项会列出当前所有 NAT 会话:

说明(仅第一行):

fxp0

表示状态绑定的接口。若状态为浮动,则显示 self

tcp

连接使用的协议。

192.168.1.35:2132

内部网络上机器的 IP 地址(192.168.1.35)。源端口(2132)显示在地址之后。这也是 IP 头部中被替换的地址。

198.51.100.1:53136

数据包被转换到的网关 IP 地址(198.51.100.1)与端口(53136)。

198.51.100.10:22

内部机器连接的目标 IP 地址(198.51.100.10)与端口(22)。

TIME_WAIT:TIME_WAIT

表示 PF 认为 TCP 连接所处的状态。

最后更新于