发新话题
打印

防火墙技术(PF:OpenBSD数据包过滤)(三)

防火墙技术(PF:OpenBSD数据包过滤)(三)

PF: 队列和优先级

------------------------------------------------------------------------------

目录

* 队列
* 日程
+ 基于类的队列
+ 优先队列
+ 随机早期检测
+ 外部拥塞告知
* 配置队列
* 为队列分配数据流
* 实例1: 小型家庭网络
* 实例2: 公司网络

------------------------------------------------------------------------------

队列

使用队列是为了按顺序保存一些等待处理的数据。在计算机网络中,当数据包由一台主机发出后,他们将进入一个等待队列,由操作系统决定哪个队列或者某个队列中的哪些包将被处理。操作系统选取包进行处理的顺序将影响网络性能。例如,一个用户打开了两个网络程序:SSH和FTP,由于SSH的时效性要求,在理想情况下SSH数据包将先于FTP数据包被处理。当用户在SSH客户端敲入一条命令时,需要有及时的反馈信息,但是FTP数据传输延时一段时间不会引起太大注意是可以忍受的,但是如果系统在处理SSH连接之前正在处理大量的FTP数据包,这时会怎样呢?SSH的数据包会留在队列中(或者由于队列长度限制被丢弃),导致SSH会话滞后。通过定义队列策略,网络带宽可以公平地在不同应用程序、用户和计算机之间分配。

注意队列只是对流出外部接口的数据包起作用。当数据包流入内部接口时再做队列将是非常迟的,因为当内部接口收到这些数据包时他们已经耗用了带宽。唯一的解决办法是在相邻的路由器启用队列,或者,如果接受到数据包的主机被当作是个路由器,那么在数据包流出该路由器的接口上启用队列。

日程

日程用来规定执行哪条队列和执行的顺序。默认情况下,OpenBSD使用先进先出(FIFO)队列。先进先出队列类似于在超市或者银行排队,先进入队列将被优先处理。新到的包被加在队列尾。如果队列满了,新到的包将被丢弃,这就是所谓的“弃尾”。

OpenBSD支持另外两种日程:

* 基于类的队列
* 优先级队列

基于类的队列

基于类的队列(CBQ)是一种排序算法,它将网络连接带宽在很多队列和类中分摊,每个队列都拥有基于源、目的地址,端口号,协议等信息分配的网络流量。一个队列可以被随意配置借用它父队列的带宽,前提是父队列没有占用该带宽。队列也有优先级,例如SSH,它的数据包将提前于含有大量数据流的队列(例如 FTP)被处理。

CBQ队列以分等级的方式部署,最高一级是根队列,用来定义全部带宽。子队列建立在根队列之下,每一个子队列可以分配根队列所规定的部分带宽。例如,定义如下队列:

Root Queue (2Mbps)

Queue A (1Mbps)
Queue B (500Kbps)
Queue C (500Kbps)

这里总带宽被设置为2Mbps,然后由子队列分割。

在子队列中仍然可以定义下一级策略。用来在不同用户中平等地分配带宽,同时对他们的流量进行分类,以便使某个协议不会抢占其他协议的带宽。具体队列结构定义如下:

Root Queue (2Mbps)

UserA (1Mbps)

ssh (50Kbps)
bulk (950Kbps)

UserB (1Mbps)

audio (250Kbps)
bulk (750Kbps)

http (100Kbps)
other (650Kbps)

注意每一层分配给各个队列的带宽之和不能超过赋予父类队列的带宽。

当父类队列由于它的一个子队列没用占用完原本分配给它的带宽时,该父类队列中的另一个子队列可以占用这部分带宽。看下列配置:

Root Queue (2Mbps)

UserA (1Mbps)

ssh (100Kbps)
ftp (900Kbps, borrow)

UserB (1Mbps)

如果UserA队列实际占用的带宽小于1Mbps(比如ssh队列没用完全占用100Kbps),则ftp队列的流量超过900Kbps时,ftp子队列将占用UserA父队列中定义的剩余的带宽。通过这种途径可以使ftp子队列超过它所定义的负荷时得到更多的带宽。随着ssh子队列流量的增加,被占用的这些带宽将被返还。

CBQ为每个队列分配一个优先级。高优先级的队列在负荷重的情况下将早于低优先级的队列被处理,同样情况应用在同父类的子队列。同优先级的队列间通过抢占方式轮流执行。例如:

Root Queue (2Mbps)

UserA (1Mbps, priority 1)

ssh (100Kbps, priority 5)
ftp (900Kbps, priority 3)

UserB (1Mbps, priority 1)

CBQ将以轮流抢占模式处理UserA和UserB队列,任何一个队列不能优先于另一个被处理。当UserA队列执行的时候,CBQ同时会处理它的子队列,这时,如果网络拥塞,ssh子队列会被优先处理,因为它的优先级高于ftp子队列。注意为什么ssh和ftp子队列不与UserA和UserB队列比较优先级,因为他们不在一个层上。

优先级队列

优先级队列(PRIQ)为一个网络接口上的多个队列逐一分配唯一的优先级。拥有高优先级的队列总是在低优先级队列前被处理。

PRIQ中的队列结构是平面的,你不可以在一个队列中定义子队列。根队列用来定义全部带宽,其他队列定义在根队列之后。请看如下实例:

Root Queue (2Mbps)

Queue A (priority 1)
Queue B (priority 2)
Queue C (priority 3)

上面定义了一个有2Mbps带宽的根队列和3个子队列。最高优先级的队列被先处理,该队列中的所有包均被处理完后,或者该队列是空,PRIQ将进一步处理下一优先级的队列。在一个队列中,各个包是按照先进先出原则进行处理。

需要注意的是当使用PRIQ时必须非常谨慎地进行设计,因为PRIQ的工作机制是先高后低,如果一个高优先级的队列收到的数据包是持续的流,那么它将延时处理低优先级的队列,更有甚者导致丢包。

随机早期检测

随机早期检测 (RED)是一种避免网络拥塞的算法,它通过确认队列没有超长来避免网络拥塞。实现方法是不停的计算队列的平均大小并与两个阈值比较,如果计算出的平均值低于小阈值将不会丢弃任何包;如果在两个阈值之间将通过计算概率丢掉一些包;换言之,如果计算的平均值越接近大阈直则被丢弃的包越多。当丢掉一些包时, RED随机选择从哪些连接丢包,占用大带宽的连接被丢包的几率高。

RED的用处非常大,因为它可以避免一种被称为全体同步的状态,也可以调整突发流量。全体同步指多个连接的数据包在同一时间被丢弃导致的吞吐量全部消失的情况。例如,如果承载10个FTP连接流量的一台路由器出现拥塞,大部分包被丢弃,总的流量将迅速下降,这并不是最好的处理方法,因为所有的FTP连接都降低了流量,换句话说,这个网络将不会再次发挥最大潜能。RED通过只在随机挑选的连接上丢包来避免上述情况。占用大带宽的连接被丢包的几率高,这样,占用大带宽的连接将受到节制,避免了拥塞,同时总流量迅速降低的
现象也不会出现。另外,RED可以处理突发流量,因为它在队列装满之前就开始丢弃数据包,当突发流量到来时,队列中有足够的空间保存新发来的数据包。

RED只能被用在传输协议有能力反馈拥塞指示的情况。在大多数情况下,这也就是说RED被用来处理TCP数据流而不是DUP或ICMP数据流。

外部拥塞告知

外部拥塞告知(ECN)与RED协作来发现两台主机网络通讯路径上的任何拥塞现象。原理是使RED在头包中设置一个标志位并返回而不是丢弃该包。假设发送端主机支持ECN,它将读到这个标志位信息并减少发出网络流量。

更多关于ECN的信息请参考RFC 3168

配置队列

自OpenBSD3.0后交互队列(ALTQ)就成为基本系统的一部分。到了OpenBSD3.3,ALTQ被集成到了PF中。ALTQ支持CBQ、PRIQ,也支持RED和ECN。

既然ALTQ被集成到了PF,那么PF就必须使队列工作。在开始一章有配置PF的介绍。

队列定义在pf.conf中,有两种指令模式:

* altq on - 在某个接口上开启队列,定义使用哪些日程,建立根队列
* queue - 定义子队列的属性

altq的语法为:

altq on interface scheduler bandwidth bw qlimit qlim \
tbrsize size queue { queue_list }

* interface - 开启队列的网络接口。
* scheduler - 使用的队列日程。可能的取值是cbq和priq。某个接口在某个时间只能启动1个日程。
* bw - 日程所能用到的所有带宽。可以使用后缀b,Kb,Mb和Gb表示,可以是具体的数值也可以是当前接口带宽的百分比。
* qlim - 队列保存数据包的最大数量。该值是可选的,默认50。
* size - 承载容量的大小,单位bytes。如果没有指定,将基于接口带宽自动设置。
* queue_list - 在根队列下建立的一个子队列列表。

实例:

altq on fxp0 cbq bandwidth 2Mb queue { std, ssh, ftp }

这条策略在接口fxp0启用CBQ,总带宽设置为2Mbps,定义了3个子队列:std,ssh和ftp。

队列指令的语法是:

queue name [on interface] bandwidth bw [priority pri] [qlimit qlim] \
scheduler ( sched_options ) { queue_list }

* name - 队列的名称,必须与altq中定义的队列列表中的某个队列名称一致。对于cbq,还可以与以前定义的队列列表中的某个名称一致。长度不能超过15个字符。
* interface - 队列所生效的网络接口,是可选项,如果没指定,将在所有接口生效。
* bw - 队列可以使用的总带宽。可以是具体数值加上后缀b,Kb,Mb和Gb来表示,也可以是总带宽的百分比。该参数只有在使用cbq日程时可用。
* pri - 队列的优先级。对于cbq,优先级的范围是0~7,对于priq,范围是0~15。0是最低的优先级,如果没定义,默认优先级为1。
* qlim - 队列可以保存的最大包数,默认50。
* scheduler - 日程,cbq或者priq。必须和根队列一致。
* sched_options - 控制日程行为的更多选项:
+ default - 定义默认队列,当包不匹配其他队列时的默认值。
+ red - 在当前队列启用RED。
+ rio - 对进/出启用RED。在这种状态下,RED会维护多个平均队列长度和平均门限值,每个IP服务质量级别对应一个。
+ ecn - 在当前队列启用ECN。
+ borrow - 队列可以向父类借用带宽值,只被用于cbq日程模式。
* queue_list - 在当前队列下建立的一系列子队列。只有在cbq日程模式下有效。

继续上述实例:

queue std bandwidth 50% cbq(default)
queue ssh { ssh_login, ssh_bulk }
queue ssh_login priority 4 cbq(ecn)
queue ssh_bulk cbq(ecn)
queue ftp bandwidth 500Kb priority 3 cbq(borrow red)

这里将设置前面已经定义的队列。Std队列分配了根队列的50%带宽,也就是1Mbps,并被设置为默认队列。Ssh队列定义了两个子队列, ssh_login和ssh_bulk。前者被分配了高于后者的优先级,并且都开启了ECN。ftp队列分配了500Kbps带宽,拥有第3优先级,当总带宽富余时它可以借用,同时也开启了RED。

为队列分配数据流

通过在PF过滤策略中增加queue关键字为队列分配流量。例如,假设某个策略集包含下面一条策略:

pass out on fxp0 from any to any port 22

符合上述策略的数据流可以通过queue关键字赋予一个特定的队列:

pass out on fxp0 from any to any port 22 queue ssh

当queue关键字应用到block时,任何TCP RST或者ICMP Unreachable数据包将被分配到特定对列。

注意队列除了可以在altq语句生效外,还可以在接口上生效:

altq on fxp0 cbq bandwidth 2Mb queue { std, ftp }
queue std cbq(default)
queue ftp bandwidth 1.5Mb

pass in on dc0 from any to any port 21 queue ftp

fxp0接口上指定了队列,但是这个队列却可以在dc0上生效。如果数据包匹配了fxp0接口外的那条pass策略,它们将被应用到ftp队列。这种用法在路由器上用处很广。

一般情况下queue关键字后面只有一个队列,如果有另外一个,这个队列将被用处理低延时的包和TCP ACK的包,这种包没有有效数据载荷。例如,当使用SSH时,SSH的登录会话将设置为低延时性,然而SCP和SFTP会话则不会。PF可以通过这些信息对属于登录会话的数据包取出并重新排列,这对于赋予登录会话的数据包高优先级非常有用。

pass out on fxp0 from any to any port 22 queue(ssh_bulk, ssh_login)

这条策略将属于SSH登录连接的数据包分配给ssh_logind队列,属于SCP和SFTP连接的数据包分配给ssh_bulk队列。由于前者拥有比后者高的优先级,所以登录连接的数据包被优先处理。

在非对称连接情况下给TCP ACK包分配一个高优先级的队列将是很有用的,比如,ADSL就是一种非对称连接,因为它的上行和下行带宽不一致。如果上行通道已满,而恰在这时开始了下载动作,下载过程的启动需要客户端返回TCP ACK包,但此时该包由于上行阻塞不能及时到达下载服务器,下载过程将会进入等待状态。试验证明,为了取得好的效果,上行队列的带宽最好设置比实际带宽小一些。例如,一个ADSL线路最大上行带宽640Kbps,那么设置根队列600Kbps的带宽效果会很好。

使用keep state的队列策略:

pass in on fxp0 proto tcp from any to any port 22 flags S/SA \
keep state queue ssh

PF将在状态表中记录ssh队列,当数据从fxp0接口流出时如果符合表中的记录它将被放入ssh队列。注意,尽管queue关键字应用到了过滤规则中,目的在于影响流出的数据包,上述策略中队列不会对流入的数据包起作用。

实例 #1: 小型家庭网络


[ Alice ] [ Charlie ]
| | ADSL
---+-----+-------+------ dc0 [ OpenBSD ] fxp0 -------- ( Internet )
|
[ Bob ]


在这个例子中,OpenBSD充当一个家庭网的网关,提供包过滤和NAT服务,家庭网中有3个客户端,因特网连接是通过ADSL实现,并有2Mbps下行和640Kbps上行的带宽。

该网络的队列策略:

* 为Bob保留玩在线游戏的80Kbps下行带宽,以减少另外两人对他的影响,并且总带宽富余的情况下可以超出该限制。
* 交互的SSH和即时信息流量要有高于其他流量的优先级。
* DNS请求和反馈数据流要有第二高的优先级。
* 流出的TCP ACK 数据包的优先级要高于其他流出数据包的优先级。

下面是对应的策略(省略了其他部分策略,如rdr、nat等):

# enable queueing on the external interface to control traffic going to
# the Internet. use the priq scheduler to control only priorities. set
# the bandwidth to 610Kbps to get the best performance out of the TCP
# ACK queue.

altq on fxp0 priq bandwidth 610Kb queue { std_out, ssh_im_out, dns_out, \
tcp_ack_out }

# define the parameters for the child queues.
# std_out - the standard queue. any filter rule below that does not
# explicitly specify a queue will have its traffic added
# to this queue.
# ssh_im_out - interactive SSH and various instant message traffic.
# dns_out - DNS queries.
# tcp_ack_out - TCP ACK packets with no data payload.

queue std_out priq(default)
queue ssh_im_out priority 4 priq(red)
queue dns_out priority 5
queue tcp_ack_out priority 6

# enable queueing on the internal interface to control traffic coming in
# from the Internet. use the cbq scheduler to control bandwidth. max
# bandwidth is 2Mbps.

altq on dc0 cbq bandwidth 2Mb queue { std_in, ssh_im_in, dns_in, bob_in }

# define the parameters for the child queues.
# std_in - the standard queue. any filter rule below that does not
# explicitly specify a queue will have its traffic added
# to this queue.
# ssh_im_in - interactive SSH and various instant message traffic.
# dns_in - DNS replies.
# bob_in - bandwidth reserved for Bob‘s workstation. allow him to
# borrow.

queue std_in cbq(default)
queue ssh_im_in priority 4
queue dns_in priority 5
queue bob_in bandwidth 80Kb cbq(borrow)


# ... in the filtering section of pf.conf ...

alice = "192.168.0.2"
bob = "192.168.0.3"
charlie = "192.168.0.4"
local_net = "192.168.0.0/24"
ssh_ports = "{ 22 2022 }"
im_ports = "{ 1863 5190 5222 }"

# filter rules for fxp0 inbound
block in on fxp0 all

# filter rules for fxp0 outbound
block out on fxp0 all
pass out on fxp0 inet proto tcp from (fxp0) to any flags S/SA \
keep state queue(std_out, tcp_ack_out)
pass out on fxp0 inet proto { udp icmp } from (fxp0) to any keep state
pass out on fxp0 inet proto { tcp udp } from (fxp0) to any port domain \
keep state queue dns_out
pass out on fxp0 inet proto tcp from (fxp0) to any port $ssh_ports \
flags S/SA keep state queue(std_out, ssh_im_out)
pass out on fxp0 inet proto tcp from (fxp0) to any port $im_ports \
flags S/SA keep state queue(ssh_im_out, tcp_ack_out)

# filter rules for dc0 inbound
block in on dc0 all
pass in on dc0 from $local_net

# filter rules for dc0 outbound
block out on dc0 all
pass out on dc0 from any to $local_net
pass out on dc0 proto { tcp udp } from any port domain to $local_net \
queue dns_in
pass out on dc0 proto tcp from any port $ssh_ports to $local_net \
queue(std_in, ssh_im_in)
pass out on dc0 proto tcp from any port $im_ports to $local_net \
queue ssh_im_in
pass out on dc0 from any to $bob queue bob_in

实例 #2: 公司网络


( IT Dept ) [ Boss‘s PC ]
| | T1
--+----+-----+---------- dc0 [ OpenBSD ] fxp0 -------- ( Internet )
| fxp1
[ COMP1 ] [ WWW ] /
| /
--+----------‘


这个例子中OpenBSD作为公司网络的防火墙,公司内部在DMZ区运行了WWW服务器,用户通过FTP上传他们的网站。IT部门有自己的子网,老板的电脑主要用来收发电子邮件和网页冲浪。防火墙通过1.5Mbps双向带宽的T1电路连接因特网,其他网段均使用快速以太网(100Mbps)。

实现上述要求的策略如下:

* 限制WWW服务器到因特网之间的双向流量——500Kbps。
* WWW服务器和内部网络之间没有流量限制。
* 赋予WWW服务器和因特网间的流量高于其他流量的优先级(例如FTP上传流量)。
* 为IT部门保留500Kbps的带宽使他们可以下载到最新的软件,同时如果总带宽富余,他们可以借用。
* 为老板访问因特网的流量赋予比其他访问因特网流量高的优先级。

下面是对应的策略(省略了其他部分策略,如rdr、nat等):

# enable queueing on the external interface to queue packets going out
# to the Internet. use the cbq scheduler so that the bandwidth use of
# each queue can be controlled. the max outgoing bandwidth is 1.5Mbps.

altq on fxp0 cbq bandwidth 1.5Mb queue { std_ext, www_ext, boss_ext }

# define the parameters for the child queues.
# std_ext - the standard queue. also the default queue for
# outgoing traffic on fxp0.
# www_ext - container queue for WWW server queues. limit to
# 500Kbps.
# www_ext_http - http traffic from the WWW server
# www_ext_misc - all non-http traffic from the WWW server
# boss_ext - traffic coming from the boss‘s computer

queue std_ext cbq(default)
queue www_ext bandwidth 500Kb { www_ext_http, www_ext_misc }
queue www_ext_http priority 3 cbq(red)
queue www_ext_misc priority 1
queue boss_ext priority 3

# enable queueing on the internal interface to control traffic coming
# from the Internet or the DMZ. use the cbq scheduler to control the
# bandwidth of each queue. bandwidth on this interface is set to the
# maximum. traffic coming from the DMZ will be able to use all of this
# bandwidth while traffic coming from the Internet will be limited to
# 1.0Mbps (because 0.5Mbps (500Kbps) is being allocated to fxp1).

altq on dc0 cbq bandwidth 100% queue { net_int, www_int }

# define the parameters for the child queues.
# net_int - container queue for traffic from the Internet. bandwidth
# is 1.0Mbps.
# std_int - the standard queue. also the default queue for outgoing
# traffic on dc0.
# it_int - traffic to the IT Dept network.
# boss_int - traffic to the boss‘s PC.
# www_int - traffic from the WWW server in the DMZ.

queue net_int bandwidth 1.0Mb { std_int, it_int, boss_int }
queue std_int cbq(default)
queue it_int bandwidth 500Kb cbq(borrow)
queue boss_int priority 3
queue www_int cbq(red)

# enable queueing on the DMZ interface to control traffic destined for
# the WWW server. cbq will be used on this interface since detailed
# control of bandwidth is necessary. bandwidth on this interface is set
# to the maximum. traffic from the internal network will be able to use
# all of this bandwidth while traffic from the Internet will be limited
# to 500Kbps.

altq on fxp1 cbq bandwidth 100% queue { internal_dmz, net_dmz }

# define the parameters for the child queues.
# internal_dmz - traffic from the internal network.
# net_dmz - container queue for traffic from the Internet.
# net_dmz_http - http traffic.
# net_dmz_misc - all non-http traffic. this is also the default queue.

queue internal_dmz # no special settings needed
queue net_dmz bandwidth 500Kb { net_dmz_http, net_dmz_misc }
queue net_dmz_http priority 3 cbq(red)
queue net_dmz_misc priority 1 cbq(default)


# ... in the filtering section of pf.conf ...

main_net = "192.168.0.0/24"
it_net = "192.168.1.0/24"
int_nets = "{ 192.168.0.0/24, 192.168.1.0/24 }"
dmz_net = "10.0.0.0/24"

boss = "192.168.0.200"
wwwserv = "10.0.0.100"

# default deny
block on { fxp0, fxp1, dc0 } all

# filter rules for fxp0 inbound
pass in on fxp0 proto tcp from any to $wwwserv port { 21, \
> 49151 } flags S/SA keep state queue www_ext_misc
pass in on fxp0 proto tcp from any to $wwwserv port 80 \
flags S/SA keep state queue www_ext_http

# filter rules for fxp0 outbound
pass out on fxp0 from $int_nets to any keep state
pass out on fxp0 from $boss to any keep state queue boss_ext

# filter rules for dc0 inbound
pass in on dc0 from $int_nets to any keep state
pass in on dc0 from $it_net to any queue it_int
pass in on dc0 from $boss to any queue boss_int
pass in on dc0 proto tcp from $int_nets to $wwwserv port { 21, 80, \
> 49151 } flags S/SA keep state queue www_int

# filter rules for dc0 outbound
pass out on dc0 from dc0 to $int_nets

# filter rules for fxp1 inbound
pass in on fxp1 proto { tcp, udp } from $wwwserv to any port 53 \
keep state

# filter rules for fxp1 outbound
pass out on fxp1 proto tcp from any to $wwwserv port { 21, \
> 49151 } flags S/SA keep state queue net_dmz_misc
pass out on fxp1 proto tcp from any to $wwwserv port 80 \
flags S/SA keep state queue net_dmz_http
pass out on fxp1 proto tcp from $int_nets to $wwwserv port { 80, \
21, > 49151 } flags S/SA keep state queue internal_dmz


------------------------------------------------------------------------------
$OpenBSD: queueing.html,v 1.21 2004/06/11 20:26:53 saad Exp $
==============================================================================

PF: 地址池和负载均衡

------------------------------------------------------------------------------

目录

* 简介
* NAT 地址池
* 外来连接负载均衡
* 输出流量负载均衡
+ 规则集实例

------------------------------------------------------------------------------

简介

地址池是提供2个以上的地址供一组用户共享的。地址池可以是rdr规则中的重定向地址;可以是nat规则中的转换地址;也可以是route-to, reply-to, 和 dup-to filter选项中的目的地址。

有4种使用地址池的方法:

* bitmask - 截取被修改地址(nat规则的源地址;rdr规则的目标地址)的最后部分和地址池地址的网络部分组合。例如:如果地址池是192.0.2.1/24,而被修改地址是10.0.0.50,则结果地址是192.0.2.50。如果地址池是192.0.2.1/25,而被修改地址是10.0.0.130,这结果地址是192.0.2.2。
* random - 从地址池中随机选择地址.
* source-hash - 使用源地址 hash 来确定使用地址池中的哪个地址。这个方法保证给定的源地址总是被映射到同一个地址池。Hash算法的种子可以在source-hash关键字后通过16进制字符或者字符串来指定。默认情况下,pfctl(8)在规则集装入时会随机产生种子。
* round-robin - 在地址池中按顺序循环,这是默认方法,也是表中定义的地址池唯一的方法。

除了round-robin方法,地址池的地址必须表达成CIDR(Classless Inter-Domain Routing )的网络地址族。round-robin 方法可以接受多个使用列表和表的单独地址。

sticky-address 选项可以在 random 和round-robin 池类型中使用,保证特定的源地址始终映射到同样的重定向地址。

NAT 地址池

地址池在NAT规则中可以被用做转换地址。连接的源地址会被转换成使用指定的方法从地址池中选择的地址。这对于PF负载一个非常大的网络的NAT会非常有用。由于经过NAT的连接对每个地址是有限的,增加附加的转换地址允许NAT网关增大服务的用户数量。

在这个例子中,2个地址被用来做输出数据包的转换地址。对于每一个输出的连接,PF按照顺序循环使用地址。


nat on $ext_if inet from any to any -> { 192.0.2.5, 192.0.2.10 }

这个方法的一个缺点是成功建立连接的同一个内部地址不会总是转换为同一个外部地址。这会导致冲突,例如:浏览根据用户的ip地址跟踪登录的用户的web站点。一个可选择的替代方法是使用source-hash 方法,以便每一个内部地址总是被转换为同样的外部地址。,要实现这个方法,地址池必须是CIDR网络地址。

nat on $ext_if inet from any to any -> 192.0.2.4/31 source-hash

这条NAT规则使用地址池192.0.2.4/31 (192.0.2.4 - 192.0.2.5)做为输出数据包的转换地址。每一个内部地址会被转换为同样的外部地址,由于source-hash关键字的缘故。

外来连接负载均衡

地址池也可以用来进行外来连接负载均衡。例如,外来的web服务器连接可以分配到服务器群。

web_servers = "{ 10.0.0.10, 10.0.0.11, 10.0.0.13 }"

rdr on $ext_if proto tcp from any to any port 80 -> $web_servers \
round-robin sticky-address

成功的连接将按照顺序重定向到web服务器,从同一个源到来的连接发送到同一个服务器。这个sticky connection会和指向这个连接的状态一起存在。如果状态过期,sticky
connection也过期。那个主机的更多连接被重定向到按顺序的下一个web服务器。

输出流量负载均衡

地址池可以和route-to过滤选项联合使用,在多路径路由协议(例如BGP4)不可用是负载均衡2个或者多个因特网连接。通过对round-robin地址池使用route-to,输出连接可以平均分配到多个输出路径。

需要收集的附加的信息是邻近的因特网路由器IP地址。这要加入到route-to选项后来控制输入数据包的目的地址。

下面的例子通过2条到因特网的连接平衡输出流量:

lan_net = "192.168.0.0/24"
int_if = "dc0"
ext_if1 = "fxp0"
ext_if2 = "fxp1"
ext_gw1 = "68.146.224.1"
ext_gw2 = "142.59.76.1"

pass in on $int_if route-to \
{ ($ext_if1 $ext_gw1), ($ext_if2 $ext_gw2) } round-robin \
from $lan_net to any keep state

route-to 选项用来在收到流量的内部接口上指定平衡的流量经过各自的网关到输出的网络接口。注意route-to 选项必须在每个需要均衡的过滤规则上出现。返回的数据包会路由到它们出去时的外部接口(这是由ISP做的),然后正常路由回内部网络。

要保证带有属于$ext_if1源地址的数据包总是路由到$ext_gw1($ext_if2 和 $ext_gw2也是同样的),下面2行必须包括在规则集中:

pass out on $ext_if1 route-to ($ext_if2 $ext_gw2) from $ext_if2 \
to any
pass out on $ext_if2 route-to ($ext_if1 $ext_gw1) from $ext_if1 \
to any

最后,NAT也可以使用在输出接口中:

nat on $ext_if1 from $lan_net to any -> ($ext_if1)
nat on $ext_if2 from $lan_net to any -> ($ext_if2)

一个完整的输出负载均衡的例子应该是这个样子:

lan_net = "192.168.0.0/24"
int_if = "dc0"
ext_if1 = "fxp0"
ext_if2 = "fxp1"
ext_gw1 = "68.146.224.1"
ext_gw2 = "142.59.76.1"

# nat outgoing connections on each internet interface
nat on $ext_if1 from $lan_net to any -> ($ext_if1)
nat on $ext_if2 from $lan_net to any -> ($ext_if2)

# default deny
block in from any to any
block out from any to any

# pass all outgoing packets on internal interface
pass out on $int_if from any to $lan_net
# pass in quick any packets destined for the gateway itself
pass in quick on $int_if from $lan_net to $int_if
# load balance outgoing tcp traffic from internal network.
pass in on $int_if route-to \
{ ($ext_if1 $ext_gw1), ($ext_if2 $ext_gw2) } round-robin \
proto tcp from $lan_net to any flags S/SA modulate state
# load balance outgoing udp and icmp traffic from internal network
pass in on $int_if route-to \
{ ($ext_if1 $ext_gw1), ($ext_if2 $ext_gw2) } round-robin \
proto { udp, icmp } from $lan_net to any keep state

# general "pass out" rules for external interfaces
pass out on $ext_if1 proto tcp from any to any flags S/SA modulate state
pass out on $ext_if1 proto { udp, icmp } from any to any keep state
pass out on $ext_if2 proto tcp from any to any flags S/SA modulate state
pass out on $ext_if2 proto { udp, icmp } from any to any keep state

# route packets from any IPs on $ext_if1 to $ext_gw1 and the same for
# $ext_if2 and $ext_gw2
pass out on $ext_if1 route-to ($ext_if2 $ext_gw2) from $ext_if2 to any
pass out on $ext_if2 route-to ($ext_if1 $ext_gw1) from $ext_if1 to any

------------------------------------------------------------------------------
$OpenBSD: pools.html,v 1.12 2004/05/07 01:55:24 nick Exp $
==============================================================================

TOP

发新话题