发新话题
打印

OpenBSD 数据包过滤 PF FAQ中文版(转)-4

OpenBSD 数据包过滤 PF FAQ中文版(转)-4

(续上)


    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 池类型中使用,保证特定的源地址始终映射到同样的重定向地址。

(待续)

TOP

发新话题