RouterOS QoS

Mason Lyu
MischievousBOSS
Published in
23 min readAug 4, 2019

Queue / Hierarchical Token Bucket / Per Connection Queue

QoS(服務質量)可設定使用者的網路使用類型來決定分配的頻寬,也可以設定重要的網路裝置取得較高的網路優先權。只要將某台電腦的上網優先權提高後,在網路頻寬吃緊的情況下 (譬如有另一台電腦在大量下載檔案),依舊可以走優先通關通道,將頻寬資源優先分配給擁有高優先權的電腦,剩餘的頻寬才給另一台在大量下載檔案的電腦。

在討論RouterOS的QoS之前,有幾個名詞我們先來解析一下

Queue

佇列,透過一個先進先出(FIFO)的緩衝區,先把要送往外的封包儲存起來後再根據條件往外送出,可以用來限制頻寬。有關Queue的詳細說明,可以參考MirkoTik Queue

HTB (Hierarchical Token Bucket)

一個採優先權制的階層式佇列模式。依照優先權來決定,將擁有較高優先權的封包往外送。有關HTB的詳細說明,可以參考MikroTik Queue — HTB

PCQ (Per Connection Queue)

一種佇列的準則,用來動態平均分配頻寬給所有的使用者。有關PCQ的詳細說明,可以參考MikroTik Queue — PCQ

我們這次要應用這些功能來實現QoS。在 [/queue] 裡面,共有三樣東西:

  1. /queue simple
  2. /queue tree
  3. /queue type

先來說說 [/queue type],這裡用來描述不同的佇列應用種類,除了預先設定的幾個之外,我們可以根據我們的需求來新增,這時我們先來設定兩個新的種類,並建構於PCQ這項演算法

/queue typeadd kind=pcq name=Download_PCQ pcq-classifier=dst-address pcq-limit=100 pcq-rate=300M pcq-total-limit=5000 pcq-src-address-mask=32 pcq-dst-address-mask=32 pcq-src-address6-mask=128 pcq-dst-address6-mask=128add kind=pcq name=Upload_PCQ pcq-classifier=src-address pcq-limit=100 pcq-rate=100M pcq-total-limit=5000 pcq-src-address-mask=32 pcq-dst-address-mask=32 pcq-src-address6-mask=128 pcq-dst-address6-mask=128

其中Download_PCQ的pcq-classifier=dst-address,表示我們要依據下載的目標地址來平均分配頻寬,也就是依據區網內每一台設備來分;而Upload_PCQ的pcq-classifier=src-address,表示我們要依據上傳的來源地址來平均分配頻寬,也是依據區網內每一台設備來分。

pcq-limit=100 代表每一台設備可以有100個封包的緩衝區,而pcq-total-limit=5000則表示我們最多支援50台設備。根據之前MikroTik提供的資料,緩衝區中每一個封包約需2200 bytes的儲存空間,因此pcq-total-limit=5000全用滿也就需要10.5MB的RAM,上傳加下載全用滿就需要21MB的RAM。有關pcq-limit設定值的影響,可以參考MikroTik Queue Size

好,接下來我們來說說 [/queue simple] 及 [/queue tree] 的不同點,這時需要MikroTik的Packet Flow v6來幫忙 http://wiki.mikrotik.com/images/f/f3/PacketFlowDiagram_v6_c.svg

  1. Queue只發生在Input Chain及PostRouting Chain
  2. [/queue tree] 限制過頻寬一次後,還會交由 [/queue simple] 再限制一次

在 [/queue simple] 裡面的規則,是序列式處理,一條規則接著一條規則運行;在 [/queue tree] 裡面的規則,是平行式處理,整體而言處理速度較快也消耗較少系統資源。我們今天拿 [/queue tree] 來使用,因為 [/queue tree] 裡面的規則是根據每個封包的標記來決定要進行的動作,所以在設定 [/queue tree] 前,我們先來介紹 [/ip firewall mangle] 裡的 “mark packet” 與 “mark connection”

mark-connection與mark-packet

注意:封包中所有的標記只會留在RouterOS內使用,一旦離開RouterOS的範圍,封包中的任何標記將被移除

封包的標記,是針對每一個滿足條件的封包進行標記,也就是說條件設定的愈多,需要進行比對的動作也就愈多;條件設定的愈少,需要進行比對的動作也就愈少,換句話說

/ip firewall mangleadd action=mark-packet chain=forward protocol=tcp dst-port=443 src-address=192.168.1.0/24 new-packet-mark=SSL out-interface=PPPoE passthrough=noadd action=mark-packet chain=forward protocol=tcp src-port=443 dst-address=192.168.1.0/24 new-packet-mark=SSL in-interface=PPPoE passthrough=no

每個封包在forward chain時,就要比對是否是TCP?是否目的埠是443?是否來源地只是來自於192.168.1.0/24網段?是否out-interface是WAN?如果都是,則添加SSL標記到這個封包。這麼多的比對動作在forward chain的每個封包都要跑過一次。如果改成

/ip firewall mangleadd action=mark-connection chain=forward protocol=tcp dst-port=443 src-address=192.168.1.0/24 new-connection-mark=SSL_conn out-interface=PPPoEadd action=mark-packet chain=forward connection-mark=SSL_conn new-packet-mark=SSL passthrough=no

則當forward chain上的第一個封包因為符合TCP, port 443, 來自192.168.1.0/24, out-interface是PPPoE之後,會被冠上SSL_conn的連線標記,此後此連線上所有的封包都會自動冠上SSL_conn的連線標記。而在forward chain上的封包在做封包標記檢查時,只要確認這個封包是否有含SSL_conn連線標記,如果有則加上SSL封包標記。比對動作由原先的每個封包四個降到每個封包一個,大幅縮減系統資源。

Queue Tree

介紹完mark-connection與mark-packet,我們來進入Queue Tree,今天要介紹的一種通用的概念。Queue的優先權共分為8類,Priority設定由1~8,其中1代表最高優先級、8代表最低優先級。

/queue tree
add max-limit=100M name=Total_Upload parent=global priority=1 queue=default
add max-limit=300M name=Total_Download parent=global priority=1 queue=defaultadd limit-at=270M max-limit=300M name=Download_Int parent=Total_Download priority=1 queue=defaultadd limit-at=30M max-limit=300M name=Download_NonInt parent=Total_Download queue=defaultadd limit-at=90M max-limit=100M name=Upload_Int parent=Total_Upload priority=1 queue=defaultadd limit-at=10M max-limit=100M name=Upload_NonInt parent=Total_Upload queue=defaultadd name=Download_Int_P1 packet-mark=Down_Int_P1 parent=Download_Int queue=Download_PCQadd name=Download_Int_P2 packet-mark=Down_Int_P2 parent=Download_Int queue=Download_PCQadd name=Download_Int_P3 packet-mark=Down_Int_P3 parent=Download_Int queue=Download_PCQadd name=Download_Int_P4 packet-mark=Down_Int_P4 parent=Download_Int queue=Download_PCQadd name=Download_Int_P5 packet-mark=Down_Int_P5 parent=Download_Int queue=Download_PCQadd name=Download_Int_P6 packet-mark=Down_Int_P6 parent=Download_Int queue=Download_PCQadd name=Download_Int_P7 packet-mark=Down_Int_P7 parent=Download_Int queue=Download_PCQadd name=Download_Int_P8 packet-mark=Down_Int_P8 parent=Download_Int queue=Download_PCQadd name=Download_NonInt_P1 packet-mark=Down_NonInt_P1 parent=Download_NonInt queue=Download_PCQadd name=Download_NonInt_P2 packet-mark=Down_NonInt_P2 parent=Download_NonInt queue=Download_PCQadd name=Download_NonInt_P3 packet-mark=Down_NonInt_P3 parent=Download_NonInt queue=Download_PCQadd name=Download_NonInt_P4 packet-mark=Down_NonInt_P4 parent=Download_NonInt queue=Download_PCQadd name=Download_NonInt_P5 packet-mark=Down_NonInt_P5 parent=Download_NonInt queue=Download_PCQadd name=Download_NonInt_P6 packet-mark=Down_NonInt_P6 parent=Download_NonInt queue=Download_PCQadd name=Download_NonInt_P7 packet-mark=Down_NonInt_P7 parent=Download_NonInt queue=Download_PCQadd name=Download_NonInt_P8 packet-mark=Down_NonInt_P8 parent=Download_NonInt queue=Download_PCQadd name=Upload_Int_P1 packet-mark=Up_Int_P1 parent=Upload_Int queue=Upload_PCQadd name=Upload_Int_P2 packet-mark=Up_Int_P2 parent=Upload_Int queue=Upload_PCQadd name=Upload_Int_P3 packet-mark=Up_Int_P3 parent=Upload_Int queue=Upload_PCQadd name=Upload_Int_P4 packet-mark=Up_Int_P4 parent=Upload_Int queue=Upload_PCQadd name=Upload_Int_P5 packet-mark=Up_Int_P5 parent=Upload_Int queue=Upload_PCQadd name=Upload_Int_P6 packet-mark=Up_Int_P6 parent=Upload_Int queue=Upload_PCQadd name=Upload_Int_P7 packet-mark=Up_Int_P7 parent=Upload_Int queue=Upload_PCQadd name=Upload_Int_P8 packet-mark=Up_Int_P8 parent=Upload_Int queue=Upload_PCQadd name=Upload_NonInt_P1 packet-mark=Up_NonInt_P1 parent=Upload_NonInt queue=Upload_PCQadd name=Upload_NonInt_P2 packet-mark=Up_NonInt_P2 parent=Upload_NonInt queue=Upload_PCQadd name=Upload_NonInt_P3 packet-mark=Up_NonInt_P3 parent=Upload_NonInt queue=Upload_PCQadd name=Upload_NonInt_P4 packet-mark=Up_NonInt_P4 parent=Upload_NonInt queue=Upload_PCQadd name=Upload_NonInt_P5 packet-mark=Up_NonInt_P5 parent=Upload_NonInt queue=Upload_PCQadd name=Upload_NonInt_P6 packet-mark=Up_NonInt_P6 parent=Upload_NonInt queue=Upload_PCQadd name=Upload_NonInt_P7 packet-mark=Up_NonInt_P7 parent=Upload_NonInt queue=Upload_PCQadd name=Upload_NonInt_P8 packet-mark=Up_NonInt_P8 parent=Upload_NonInt queue=Upload_PCQ

在這情況下,只要設定好packet-mark,自然會落入我們規劃好的優先權範圍內,所以在這之前,我們要先加入以下設定

/ip firewall mangle
add action=mark-packet chain=prerouting connection-mark=Int_P1 new-packet-mark=Down_Int_P1 in-interface=PPPoE passthrough=no
add action=mark-packet chain=prerouting connection-mark=Int_P2 new-packet-mark=Down_Int_P2 in-interface=PPPoE passthrough=noadd action=mark-packet chain=prerouting connection-mark=Int_P3 new-packet-mark=Down_Int_P3 in-interface=PPPoE passthrough=noadd action=mark-packet chain=prerouting connection-mark=Int_P4 new-packet-mark=Down_Int_P4 in-interface=PPPoE passthrough=noadd action=mark-packet chain=prerouting connection-mark=Int_P5 new-packet-mark=Down_Int_P5 in-interface=PPPoE passthrough=noadd action=mark-packet chain=prerouting connection-mark=Int_P6 new-packet-mark=Down_Int_P6 in-interface=PPPoE passthrough=noadd action=mark-packet chain=prerouting connection-mark=Int_P7 new-packet-mark=Down_Int_P7 passthrough=noadd action=mark-packet chain=prerouting connection-mark=Int_P8 new-packet-mark=Down_Int_P8 in-interface=PPPoE passthrough=noadd action=mark-packet chain=prerouting connection-mark=NonInt_P1 new-packet-mark=Down_NonInt_P1 in-interface=PPPoE passthrough=noadd action=mark-packet chain=prerouting connection-mark=NonInt_P2 new-packet-mark=Down_NonInt_P2 in-interface=PPPoE passthrough=noadd action=mark-packet chain=prerouting connection-mark=NonInt_P3 new-packet-mark=Down_NonInt_P3 in-interface=PPPoE passthrough=noadd action=mark-packet chain=prerouting connection-mark=NonInt_P4 new-packet-mark=Down_NonInt_P4 in-interface=PPPoE passthrough=noadd action=mark-packet chain=prerouting connection-mark=NonInt_P5 new-packet-mark=Down_NonInt_P5 in-interface=PPPoE passthrough=noadd action=mark-packet chain=prerouting connection-mark=NonInt_P6 new-packet-mark=Down_NonInt_P6 in-interface=PPPoE passthrough=noadd action=mark-packet chain=prerouting connection-mark=NonInt_P7 new-packet-mark=Down_NonInt_P7 in-interface=PPPoE passthrough=noadd action=mark-packet chain=prerouting connection-mark=NonInt_P8 new-packet-mark=Down_NonInt_P8 in-interface=PPPoE passthrough=noadd action=mark-packet chain=postrouting connection-mark=Int_P1 new-packet-mark=Up_Int_P1 out-interface=PPPoE passthrough=noadd action=mark-packet chain=postrouting connection-mark=Int_P2 new-packet-mark=Up_Int_P2 out-interface=PPPoE passthrough=noadd action=mark-packet chain=postrouting connection-mark=Int_P3 new-packet-mark=Up_Int_P3 out-interface=PPPoE passthrough=noadd action=mark-packet chain=postrouting connection-mark=Int_P4 new-packet-mark=Up_Int_P4 out-interface=PPPoE passthrough=noadd action=mark-packet chain=postrouting connection-mark=Int_P5 new-packet-mark=Up_Int_P5 out-interface=PPPoE passthrough=noadd action=mark-packet chain=postrouting connection-mark=Int_P6 new-packet-mark=Up_Int_P6 out-interface=PPPoE passthrough=noadd action=mark-packet chain=postrouting connection-mark=Int_P7 new-packet-mark=Up_Int_P7 passthrough=noadd action=mark-packet chain=postrouting connection-mark=Int_P8 new-packet-mark=Up_Int_P8 out-interface=PPPoE passthrough=noadd action=mark-packet chain=postrouting connection-mark=NonInt_P1 new-packet-mark=Up_NonInt_P1 out-interface=PPPoE passthrough=noadd action=mark-packet chain=postrouting connection-mark=NonInt_P2 new-packet-mark=Up_NonInt_P2 out-interface=PPPoE passthrough=noadd action=mark-packet chain=postrouting connection-mark=NonInt_P3 new-packet-mark=Up_NonInt_P3 out-interface=PPPoE passthrough=noadd action=mark-packet chain=postrouting connection-mark=NonInt_P4 new-packet-mark=Up_NonInt_P4 out-interface=PPPoE passthrough=noadd action=mark-packet chain=postrouting connection-mark=NonInt_P5 new-packet-mark=Up_NonInt_P5 out-interface=PPPoE passthrough=noadd action=mark-packet chain=postrouting connection-mark=NonInt_P6 new-packet-mark=Up_NonInt_P6 out-interface=PPPoE passthrough=noadd action=mark-packet chain=postrouting connection-mark=NonInt_P7 new-packet-mark=Up_NonInt_P7 out-interface=PPPoE passthrough=noadd action=mark-packet chain=postrouting connection-mark=NonInt_P8 new-packet-mark=Up_NonInt_P8 out-interface=PPPoE passthrough=no

在這樣的方式之下,我們不再針對每一個特定連線條件而單獨給它一個優先級,取而代之的是通用式,換句話說,我們會根據特定連線條件給封包一個落於上述區間的連線標記。舉個例子,假設要設定通過port 80的HTTP連線為優先級3,我們就可以這樣設定

/ip firewall mangle
add action=mark-connection chain=forward dst-port=80 new-connection-mark=Int_P3 out-interface=PPPoE protocol=tcp

又或者要設定BT的優先級是最低的,我們可以這樣設定

/ip firewall mangleadd action=mark-connection chain=forward new-connection-mark=NonInt_P8 p2p=all-p2padd action=mark-connection chain=forward dst-port=4662,16881 new-connection-mark=NonInt_P8 protocol=tcpadd action=mark-connection chain=forward dst-port=4672,6881 new-connection-mark=NonInt_P8 protocol=udp

藉由這樣的方式,未來我們想針對某些條件設定優先權時,就可以只在mark-connection動手腳就好,此外,拿BT與HTTP來作示範,當有人在上網、有人在下載BT時,這是瀏覽網頁的優先權就會比下載BT的高,而當多人同時下載BT時,還記得我們在Queue Tree時的 [queue=Download_PCQ]嗎?每一台下載BT的設備也會取得平均分配的頻寬,而又不影響到其他人上網瀏覽網頁的權利,同時瀏覽網頁的所有設備也會取得平均分配的頻寬。

記憶體的使用,依照先前計算的若上傳加下載的PCQ要用掉21MB,我們在Queue Tree裡設定8組的Interactive及8組的Non-Interactive,如果全部用滿,則約需21MBx16=336MB的RAM。

--

--