--------------------------------------------------------------------------------
目录
介绍
NAT怎样工作
NAT和包过滤
IP转发
配置NAT
双向映射(1:1映射)
例外的转换规则
检查NAT状态
--------------------------------------------------------------------------------
介绍
网络地址转换(NAT)是将一(或多)个网络整体映射到一个IP地址的方法。当Internet服务提供商提供给你的IP地址数量少于需访问Internet的主机数量时NAT是必须的。NAT的描述请参看 RFC 1631的"The IP Network Address Translator (NAT)."
NAT允许你使用保留的地址段, 在 RFC 1918, "Address Allocation for Private Internets."对其进行了典型性地描述。你的内部网络可以使用下列的一个或多个网络地址段:
代码: 全选
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)
OpenBSD系统要进行NAT至少需要两个网络适配器, 一个连接Internet, 另一个连接内部网络。 NAT将转换来自内部网络的请求, 所以看起来它们似乎都来自你的OpenBSD NAT系统。
NAT怎样工作
当一个内部网络的客户端与Internet上的一台计算机通讯时, 它发送IP数据包到那台计算机。这些数据包包含了所有抵达所需的地址信息。NAT会参与修改这些信息:
源地址 (例如, 192.168.1.35)
源TCP或UDP端口(例如, 2132)
当数据包流经NAT网关时将会被修改, 所以对外它们看起来像是发自NAT网关的数据包。NAT网关会将这些变化记录在自己的状态表中以便它可以 a)对回应的数据包逆向操作(还原)和 b) 确保返回的数据包不被防火墙阻止。例如, 可能产生如下的变化:
源地址: 替换为网关的外部地址(例如, 24.5.0.5)
源端口: 替换为随机指定的网关未使用端口(例如, 53136)
无论内部的机器和外部的主机都不知道这些转换步骤。对内部主机来说, NAT系统只是一个Internet网关;对Internet上的主机来说, 这些数据包看起来直接来自NAT系统; 它们甚至无法察觉存在一个这样的内部工作站。
当Internet主机回应内网机器的数据包时, 这些回应数据包会被冠以NAT网关的外部IP地址(24.5.0.5)和转换端口(53136)。NAT网关会搜索状态表, 确认这些回应的数据包是否匹配一个已经建立的连接。状态表内基于IP地址和端口的匹配项会告诉PF这些数据包属于位于192.168.1.35的内部机器发起的连接, PF将会对回应的数据包进行逆向修改操作, 并将修改后的回应数据包转发给内部机器。
ICMP数据包的转换过程类似上面的操作, 但是不修改源端口。
NAT和包过滤
注意: 转换后的数据包必须还途径过滤引擎, 并且根据预先定义的过滤规则判断允许或禁止其通过。唯一的例外是在NAT规则里使用了pass 关键字, 这将使被NAT的数据包不经检查而直接通过过滤引擎。
还需要知道的是, 因为转换发生在过滤之前, 过滤引擎看见的是带有转换地址和端口的被转换的数据包, 这在 NAT怎样工作.里有相关的描述。
IP转发
因为NAT总是用于路由器和网关, 所有可能必须在OpenBSD机器上启用IP转发以便让数据可以在网络接口间传送, 使用sysctl机制启用IP转发:
代码: 全选
# sysctl net.inet.ip.forwarding=1
# sysctl net.inet6.ip6.forwarding=1 (如果使用 IPv6)
要使这些变化永久生效, 将下面的两行加入 /etc/sysctl.conf:
代码: 全选
net.inet.ip.forwarding=1
net.inet6.ip6.forwarding=1
这两行在默认安装时已经有了, 不过是被注释的(最前面有#)。删除#并保存文件, 重新启动后IP转发据生效了。
配置NAT
通常 pf.conf 文件里的NAT规则看起来像这样:
代码: 全选
nat [pass] [log] on interface [af] from src_addr [port src_port] to \
dst_addr [port dst_port] -> ext_addr [pool_type] [static-port]
nat
开始启用NAT规则的关键字。
pass
导致被转换的数据包全部旁通于过滤规则。(不受过滤规则限制)
log
通过 pflogd(8).记录匹配的数据包。通常仅记录第一个匹配的数据包。如果要记录所有匹配的数据包使用 log (all).
interface
被转换数据包流经的网络接口名称或组。
af
地址族, inet表示IPv4;inet6表示IPv6。PF通常基于源/目标地址有能力检测这个参数。
src_addr
数据包将被转换的源(内部)地址。这个源地址可以被指定为:
一个IPv4或IPv6地址。
一个 CIDR 网段。
当规则集被载入后, 一个完全限定域名将通过DNS解析出来。所有解析出的IP地址将在规则中取代这个域名。
一个网络接口或组的名称。载入规则后所有分派给此接口的IP地址会取代规则中的网络接口或组的名称。
一个网络接口加上 /子网掩码 (例如, /24)。 所有此接口上的IP地址与子网掩码组合成一个CIDR网段, 此地址段会替换规则中的网络接口/子网掩码。
一个带有如下任何一个修饰符得网络接口名称:
:network - 代替CIDR网段(例如, 192.168.0.0/24)
:broadcast - 代替网络广播地址(例如, 192.168.0.255)
:peer - 代替点到点链接中对方的IP地址
另外, :0 修饰符可以附加到一个接口名称或上面任何修饰符的后面, 它指示PF将规则中的名称替换为IP地址时不包含别名的IP地址。这些修饰符也可以应用在括号内的接口上。 例子: fxp0:network:0
一个表格。
任何上面的规则加上了否定修饰符 ! ("not") 修饰符。
使用一个 列表 表示的一组地址。
关键字 any 代表所有地址。
src_port
分别代表第四层包头的源端口。端口可以指定为:
一个1到65535之间的数字
一个 /etc/services 内指定的有效的服务名称
用 列表 表示的一组端口
一个范围:
!= (不等于)
< (小于)
> (大于)
<= (小于等于)
>= (大于等于)
>< (范围内)
<> (范围外)
最后这两个是二元运算符(带有两个参数) 并且这两个参数不包含在范围内。
: (包含在范围内)
也是二元运算符, 但是两个参数包含在范围内。
port 选项不经常用在nat规则里, 因为不管使用那个端口, 最终目的是NAT所有通讯。
dst_addr
被转换数据包的目的地址。目的地址的指定方法同源地址。
dst_port
包含在数据包头第四层目的端口, 指定方法同源端口。
ext_addr
NAT网关上的外部(转换)地址, 数据包将被转换为此地址。外部地址可以被指定为:
一个IPv4或IPv6地址。
一个 CIDR 网段。
当规则集被载入后, 一个可以通过DNS解析的有效的域名。所有解析出的IP地址将在规则中取代这个域名。
外部网络接口的名称。 载入规则后所有分派给此接口的IP地址会取代规则中的网络接口或组的名称。
在括号()内的外部网络接口名称。 这将告诉PF当次网络接口的IP地址变化时更新规则。这在外部接口通过DHCP或拨号获得IP地址的情况下非常有用, 因为这样不用在每次IP地址变化时重新载入规则。
一个带有以下修饰符之一的网络接口名称:
:network - 代替CIDR网段(例如, 192.168.0.0/24)
:peer - 代替点到点链接中对方的IP地址
另外, :0 修饰符可以附加到一个接口名称或上面任何修饰符的后面, 它指示PF将规则中的名称替换为IP地址时不包含别名的IP地址。 这些修饰符也可以应用在括号内的接口上。 例子: fxp0:network:0
使用一个 列表 表示的一组地址。.
pool_type
指定用于转换的 地址池 类型。
static-port
告诉PF不转换TCP和UDP数据包的源端口。
通常使用的NAT规则的基本格式也许像这样:
代码: 全选
nat on tl0 from 192.168.1.0/24 to any -> 24.5.0.5
这条规则是说在tl0接口上执行NAT, 也就是将所有来自192.168.1.0/24网段的数据包的IP地址转换成24.5.0.5。
虽然上面的格式没错, 但是这不是推荐的格式, 这样维护起来很困难, 因为任何外部或内部网络的数字变化都需要重新修改这条规则。请读者可以和下面更易于维护的这行规则比较一下(tl0 是外部接口, dc0 是内部接口):
代码: 全选
nat on tl0 from dc0:network to any -> tl0
这行的优势相当明显: 你可以改变任何接口的IP地址而无需更改规则。
当像上面这样将转换地址指定为一个接口名称时, 这个IP地址在载入 pf.conf 文件时已经确定了, 不是在运行的时候。如果你使用DHCP配置你的外部接口, 这可能是个问题, 如果你被分配的IP地址变化了, NAT会继续向老的IP地址转换出站数据包, 这会导致出站连接失效。要解决这个问题, 你可以通过给此接口名称加上括号的方式告诉PF自动更新转换地址:
代码: 全选
nat on tl0 from dc0:network to any -> (tl0)
这种方法在转换IPv4和IPv6这两种地址时全有效。
双向映射(1:1映射)
一个双向映射可以通过 binat 规则建立。一个 binat 规则在内部IP地址和外部IP地址之间建立一个一对一的映射。这是有益处的, 例如:用自己的外部IP地址在Internet上提供一个web服务器, Internet到外部地址的连接将被转换为内部地址并且来自web服务器的连接(例如DNS请求)将被转换为外部地址, 在使用 binat 规则时TCP和UDP端口从不会被更改, 就像使用NAT规则时一样。(译者:没理解, 难道是web服务器在NAT规则里不能修改端口?)
例子:
代码: 全选
web_serv_int = "192.168.1.100"
web_serv_ext = "24.5.0.6"
binat on tl0 from $web_serv_int to any -> $web_serv_ext
例外的转换规则
在转换规则里使用 no 关键字可以设置例外的情况, 例如, 上面的例子改成像这样:
代码: 全选
no nat on tl0 from 192.168.1.208 to any
nat on tl0 from 192.168.1.0/24 to any -> 24.2.74.79
那么除了192.168.1.208, 整个192.168.1.0/24网段上的数据包(的IP地址)全转换成了外部地址24.2.74.79。
注意第一条规则胜利了(权限高); 如果有一条带 no 关键字的规则, 则数据包不会被转换。 no 关键字同样可以用于 binat 和 rdr 规则。
检查NAT状态 Status
要查看正在运行的NAT转换可以使用 pfctl(8) 工具加上 -s 状态选项。这个选项将列出所有当前的NAT会话:
代码: 全选
# pfctl -s state
fxp0 TCP 192.168.1.35:2132 -> 24.5.0.5:53136 -> 65.42.33.245:22 TIME_WAIT:TIME_WAIT
fxp0 UDP 192.168.1.35:2491 -> 24.5.0.5:60527 -> 24.2.68.33:53 MULTIPLE:SINGLE
解释 (仅首行):
fxp0
表示状态被绑定的接口, 如果状态是 浮动的 会出现self字句。
TCP
连接使用的协议。
192.168.1.35:2132
这个IP地址(192.168.1.35)是机器在内网上的IP地址, 源端口为2132。IP头中这个地址将被替换掉。
24.5.0.5:53136
IP地址(24.5.0.5)和端口(53136)是网关的地址和端口, 所有的内部数据包的IP地址和端口将全被转换为此IP和端口。
65.42.33.245:22
这个IP地址(65.42.33.245)和端口(22)是内部机器要访问的。
TIME_WAIT:TIME_WAIT
这表示PF认为此TCP连接应处于的状态。