- 论坛徽章:
- 0
|
高级配置
PF: 队列和优先级
------------------------------------------------------------------------------
队列
使用队列是为了按顺序保存一些等待处理的数据。在计算机网络中,当数据包由一台主机发出后,他们将进入一个等待队列,由操作系统决定哪个队列或者某个队列中的哪些包将被处理。数据包的处理顺序将影响网络性能。例如,一个用户打开了两个网络程序:SSH和FTP,由于SSH的时效性要求,在理想情况下SSH数据包将先于FTP数据包被处理。通过定义队列策略,网络带宽可以公平地分配。
注意队列只是对流出外部接口的数据包起作用。当数据包流入内部接口时再做队列将是非常迟的,因为耗用了带宽。唯一的解决办法是在相邻的路由器启用队列,或者主机被当作是个路由器,在数据包流出的接口上启用队列。
日程
日程用来规定执行哪条队列和顺序。默认情况下,OpenBSD使用先进先出(FIFO)队列。类似于排队上车,先到有位座,后到的只能等待。如果队列满了,新到的包将被丢弃,这就是所谓的“弃尾”。
OpenBSD支持另外两种日程:
* 基于类的队列
* 优先级队列
基于类的队列
基于类的队列(CBQ)是一种排序算法,它将网络连接带宽在很多队列和类中分摊,每个队列都拥有基于源、目的地址,端口号,协议等信息分配的网络流量。一个队列可以被随意配置带宽(借用它父队列带宽),前提是父队列没有占用该带宽。队列也有优先级,例如SSH和FTP,SSH数据包将优先被处理。
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)
比如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)为一个网络接口上的多个队列逐一分配唯一的优先级。拥有高优先级的队列总是在低优先级队列前被处理。
不可以在一个队列中定义子队列。根队列用来定义全部带宽,其他队列定义在根队列之后。请看如下实例:
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
配置队列
交互队列(ALTQ)支持CBQ、PRIQ,也支持RED和ECN。
ALTQ队列定义在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。前者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: 小型家庭网络
充当一个家庭网的网关,提供包过滤和NAT服务,家庭网中有3个客户端,因特网连接是通过ADSL实现,并有2Mbps下行和640Kbps上行的带宽。
^-^ (参见pf中文手册)
^-^
实例 #2: 公司网络
作为公司网络的防火墙,公司内部在DMZ区运行了WWW服务器,用户通过FTP上传他们的网站。IT部门有自己的子网,老板的电脑主要用来收发电子邮件和网页冲浪。防火墙通过1.5Mbps双向带宽的T1电路连接因特网,其他网段均使用快速以太网(100Mbps)。
^-^ (参见pf中文手册)
^-^
============================================================================== |
|