Unix Like, 操作系统, 网络

QoS —— 交互式应用最低延迟,HTTP(S)、IMAP等常用服务优先,迅雷、P2P受限

前言

使用迅雷全速下载时,其余网络应用的体验几乎约等于断网状态。

全速下载时,迅雷并没有占用太多上行带宽,于路由器上观察上行速率,同样正常:

可见全速下载时出现的现象,问题在于下行。

迅雷全网搜索资源,这意味着会有许多来自不同地方的计算机,以高速率往我的计算机发来大量数据包。ISP处的队列早已被这些P2P数据包塞满,而SSH、网页等服务的数据包速率不高,根本不够P2P数据包竞争,这些数据包到达ISP处时,只能由于队列已满而被丢弃。

 

其实这个问题,并没有什么好的解决办法,因为ISP处的队列我们没有权限控制。

我们所能做的,只是控制P2P的速率,避免因ISP处队列满而使得数据包被丢弃。

不过,上行队列我们拥有完全控制权,倒可以自由规划。

蓝图

首先,我的主要目的是,当私有网络内有人使用迅雷等进行全速下载时,这个下载不会影响交互式应用、网页流量等,不受影响终端的包括下载者自己,其次,也要保证下载流的下行速率。

这是一项很有挑战性的任务。

下行流量大致可以分为三类:

  • 最低延迟
  • 优先传送
  • 受限

这里没有普通流量这一类,因为普通流量在P2P软件,尤其是迅雷面前尤其不好区分,我尝试过Deep Packet Inspection,迅雷的流量绝大部分被分类到了Other里面。因此,与其花心思、花系统资源去区分这些“普通流量”,还不如直接使用白名单机制,把最低延迟、优先传送的区分出来。

上行流量:

  • 最低延迟
  • 优先传送
  • 普通
  • 受限

最低延迟

最低延迟的数据包,可能会以IP Header中的TOS来标记,但抓包分析发现,基本上没有使用此字段的数据包,甚至是SSH(但内网的SSH有把TOS设为0x10,表示最低延迟,可能是公网有路由清空了该字段)。所以说,只有KCPTUN之类的会“滥用”该字段,使用该字段区分最低延迟数据包意义不大。

TCP三次握手比较关键的是SYN、SYN & ACK,带有SYN和SYN & ACK标记的我们可以归类为最低延迟,此外ACK的小包(小于64字节),我们也可以归类为最低延迟。

SSH,DNS,远程桌面,QQ以及微信等交互式服务,归类为最低延迟。

优先传送

以下协议归类为优先传送:

  • HTTP
  • HTTPS
  • SMTP
  • IMAP
  • GIT

这里有个问题,如果使用迅雷进行HTTP下载,迅雷也会全网搜索HTTP资源,这里正常的HTTP流量与迅雷的HTTP流量不是很好区分。

一个最大的特征是,迅雷可能会与同一个IP建立多个TCP连接,据此可把迅雷下载用的TCP连接归类到受限。

对于单连接的,这里没有好的办法,有以下几个原因:

根据速率区分:前面已提到迅雷会全网搜索资源,意味着迅雷会构建很多TCP连接,单个TCP连接的速率不会很高。阈值低了,影响正常网页流量,高了,无效。

根据传输流量区分:首先HTTP 1.1支持Keep Alive,这意味着对于正常网页的TCP连接来说,流量达到阈值也是有可能的。

上述二者结合:如果我一个网站同时打开多几个页面,岂不是一样被归类为受限?况且现在一个页面数MB,也很正常吧?

根据内网IP构建的TCP连接数区分:我的目的是不要让迅雷影响正常的应用,如果让某个TCP连接数多的IP归类到受限,岂不是不能实现我这个目的?

普通

本分类仅用于上行,未被归类的均归于普通类。

受限

对于下行,未被归类的,均为受限。

P2P的上行发送的都是大于1350字节的UDP数据包,把这些数据包归类为受限即可。

实现

我使用的是中国电信的带宽

下行:100 Mbps,上行:20 Mbps。

互联网接口:eth0,私有网络接口:switch0。

 

首先设置以下数个变量,方便后面的命令使用:

internetDev="eth0" # 互联网接口
privateNetDev="switch0" # 私有网络接口

uploadRate="20mbit" # 互联网上行带宽
uploadP2PRate="1mbit" # P2P上行带宽
uploadEmerRate=${uploadRate} # 最低延迟上行带宽
uploadPrioRate=${uploadRate} # 优先传送上行带宽
uploadNormalRate="10mbit" # 普通流量上行带宽

downloadRate="100mbit" # 互联网下行带宽
downloadP2PRate="80mbit" # P2P下行带宽
downloadEmerRate=${downloadRate} # 最低延迟传送下行带宽
downloadPrioRate=${downloadRate} # 优先传送下行带宽

这里我主要的目的在于不要让P2P把ISP处的队列占满,其次对于正常的流量,并不会占用太多带宽,因此正常流量不作速率限制,仅分配不同的优先级,而P2P则限制速率,设置最低的优先级。

区分P2P上行

把大于1350字节的UDP数据包,都标记为100。

# For QoS, MARK 100
iptables -t mangle -N MARK_100
iptables -t mangle -A MARK_100 -p udp -m length ! --length 0:1350 -j MARK --set-mark 100
iptables -t mangle -I PREROUTING -i ${privateNetDev} -j MARK_100

区分迅雷TCP流

P2P的不需要区分,因为下行使用的是白名单机制。

主要问题在于迅雷的HTTP下载,实难区分,这里用了一个不太尽人意的方法,把迅雷的TCP流标记为200:

# For QoS, MARK 200
iptables -t mangle -N PRIVATE_NET_MARK_200
iptables -t mangle -A PRIVATE_NET_MARK_200 -p tcp -j CONNMARK --restore-mark
iptables -t mangle -A PRIVATE_NET_MARK_200 -m mark --mark 200 -j RETURN
iptables -t mangle -A PRIVATE_NET_MARK_200 -m connlimit --connlimit-above 4 --connlimit-mask 24 --connlimit-saddr -m hashlimit --hashlimit-above 512kb/second --hashlimit-burst 5mb --hashlimit-mode srcip,dstip --hashlimit-dstmask 24 --hashlimit-srcmask 32 --hashlimit-name rateLimit -j MARK --set-mark 200
iptables -t mangle -A PRIVATE_NET_MARK_200 -j CONNMARK --save-mark
iptables -t mangle -I POSTROUTING -o ${privateNetDev} -j PRIVATE_NET_MARK_200

首先,不能在互联网接口上对TCP流进行归类,因为在互联网接口上,所有TCP流的目的地都是我们互联网接口的IP,这导致的问题就是无法独立统计单个内网IP对单个远程IP的链接数。想一想,如果网内有多个人同时访问某个网站,是不是有可能被归类为受限?

因此这里在私有网络的接口上进行归类。

归类用到了两个模块,分别是connlimit,hashlimit。

connlimit模块仅能统计单端的链接数,hashlimit模块能双端统计,但是只能统计速率,因此配合使用,以接近限制端对端链接数的效果。

这里connlimit用到的参数,表示远程一个/24与本地构建的TCP流超过四的情况;hashlimit的参数表示远程一个/24与本地一个IP突发5MB后,数据传输速率>512kb/s的情况。

这里还用到了connmark模块,这个模块用于记录TCP流的标记,下次遇到相同的TCP流时,取上次的标记即可,无需再使用connlimit与hashlimit模块进行判断,因为这两个模块资源消耗不低。

构建上行规则

这里使用了HTB对速率进行限制,并在每个类下面安置了一个SFQ队列,以公平分配一个队列中各流的带宽:

tc qdisc del dev ${internetDev} root # 删除现有的规则
tc qdisc add dev ${internetDev} root handle 1: htb default 50 # 默认归类到类50

tc class add dev ${internetDev} parent 1: classid 1:1 htb rate ${uploadRate}
tc class add dev ${internetDev} parent 1:1 classid 1:100 htb rate ${uploadP2PRate} ceil ${uploadP2PRate} prio 7 # P2P 优先级7
tc class add dev ${internetDev} parent 1:1 classid 1:10 htb rate ${uploadEmerRate} prio 0 # 最低延迟 优先级0
tc class add dev ${internetDev} parent 1:1 classid 1:20 htb rate ${uploadPrioRate} prio 2 # 优先传送 优先级2
tc class add dev ${internetDev} parent 1:1 classid 1:50 htb rate ${uploadNormalRate} prio 5 # 普通 优先级5

# 随机公平队列
tc qdisc add dev ${internetDev} parent 1:100 handle 100: sfq perturb 10
tc qdisc add dev ${internetDev} parent 1:10 handle 10: sfq perturb 10
tc qdisc add dev ${internetDev} parent 1:20 handle 20: sfq perturb 10
tc qdisc add dev ${internetDev} parent 1:50 handle 50: sfq perturb 10

接下来就把数据包归类,我选择使用u32选择器。

u32选择器强大,同时也难用。

u8, u16都会转换为u32,uX跟着一个值与掩码,at后面是偏移量,偏移从IP Header开始。

掩码将会与数据包偏移位置处进行位于,再与给定的值进行比较。

更多u32选择器的细节,见u32选择器的手册页:Universal 32bit classifier in tc(8) LinuxUniversal 32bit classifier in tc(8)

分类规则:

# Match TCP SYN
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 1 u32 \
match u8 0x10 0xff at 33 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match TCP ACK Small Packet (< 64 Bytes)
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 1 u32 \
match u8 0x10 0xff at 33 \
match u8 0x06 0xff at 9 \
match u16 0x0000 0xffc0 at 2 \
flowid 1:10

# Match TCP DESTINATION PORT 22
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0016 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match TCP DESTINATION PORT 23
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0017 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match TCP DESTINATION PORT 25
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0019 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match TCP DESTINATION PORT 53
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0035 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match UDP DESTINATION PORT 53
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0035 0xffff at 22 \
match u8 0x11 0xff at 9 \
flowid 1:10

# Match TCP DESTINATION PORT 80
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0050 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match TCP DESTINATION PORT 443
tc filter add dev ${internetDev} protocol ip parent 1: pref 10 u32 \
match u16 0x01bb 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match UDP DESTINATION PORT 443 (QUIC Protocol)
tc filter add dev ${internetDev} protocol ip parent 1: pref 10 u32 \
match u16 0x01bb 0xffff at 22 \
match u8 0x11 0xff at 9 \
flowid 1:20

# Match TCP DESTINATION PORT 465
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x01d1 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match TCP DESTINATION PORT 993
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x03e1 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match TCP DESTINATION PORT 3306
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0cea 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match TCP DESTINATION PORT 3389
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0d3d 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match UDP DESTINATION PORT 3389
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0d3d 0xffff at 22 \
match u8 0x11 0xff at 9 \
flowid 1:10

# Match UDP DESTINATION PORT 8000 (QQ Port)
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x1f40 0xffff at 22 \
match u8 0x11 0xff at 9 \
flowid 1:10

# Match TCP DESTINATION PORT 8080 (WeChat Port)
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x1f90 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match TCP DESTINATION PORT 9418 (GIT)
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x24ca 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Limit UDP Huge Packet Rate
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 20 u32 \
match mark 0x0064 0x00ff \
flowid 1:100

构建下行规则

队列的构建与上面同理,只是少了一个普通流量的类:

tc qdisc del dev ${privateNetDev} root # 删除现有的队列
tc qdisc add dev ${privateNetDev} root handle 1: htb default 100 # 默认归类到100

tc class add dev ${privateNetDev} parent 1: classid 1:1 htb rate ${downloadRate}
tc class add dev ${privateNetDev} parent 1:1 classid 1:100 htb rate ${downloadP2PRate} ceil ${downloadP2PRate} prio 7 # P2P
tc class add dev ${privateNetDev} parent 1:1 classid 1:10 htb rate ${downloadEmerRate} prio 0 # 最低延迟
tc class add dev ${privateNetDev} parent 1:1 classid 1:20 htb rate ${downloadPrioRate} prio 2 # 优先传送

# 随机公平队列
tc qdisc add dev ${privateNetDev} parent 1:100 handle 100: sfq perturb 10
tc qdisc add dev ${privateNetDev} parent 1:10 handle 10: sfq perturb 10
tc qdisc add dev ${privateNetDev} parent 1:20 handle 20: sfq perturb 10

归类:

# Match TCP SYN ACK
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 1 u32 \
match u8 0x12 0xff at 33 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match TCP ACK Small Packet (< 64 Bytes)
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 1 u32 \
match u8 0x10 0xff at 33 \
match u8 0x06 0xff at 9 \
match u16 0x0000 0xffc0 at 2 \
flowid 1:10

# Match multip thread download
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 5 u32 \
match mark 0x00c8 0x00ff \
flowid 1:100

# Match TCP SOURCE PORT 22
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0016 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match TCP SOURCE PORT 23
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0017 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match TCP SOURCE PORT 25
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0019 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match TCP SOURCE PORT 53
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0035 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match UDP SOURCE PORT 53
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0035 0xffff at 20 \
match u8 0x11 0xff at 9 \
flowid 1:10

# Match TCP SOURCE PORT 80
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0050 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match TCP SOURCE PORT 443
tc filter add dev ${privateNetDev} protocol ip parent 1: pref 10 u32 \
match u16 0x01bb 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match UDP SOURCE PORT 443 (QUIC Protocol)
tc filter add dev ${privateNetDev} protocol ip parent 1: pref 10 u32 \
match u16 0x01bb 0xffff at 20 \
match u8 0x11 0xff at 9 \
flowid 1:20

# Match TCP SOURCE PORT 465
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x01d1 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match TCP SOURCE PORT 993
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x03e1 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match TCP SOURCE PORT 3306
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0cea 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match TCP SOURCE PORT 3389
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0d3d 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match UDP SOURCE PORT 3389
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0d3d 0xffff at 20 \
match u8 0x11 0xff at 9 \
flowid 1:10

# Match UDP SOURCE PORT 8000 (QQ Port)
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x1f40 0xffff at 20 \
match u8 0x11 0xff at 9 \
flowid 1:10

# Match TCP SOURCE PORT 8080 (WeChat Port)
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x1f90 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match TCP SOURCE PORT 9418 (GIT)
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x24ca 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:20

完整的Script

#!/bin/bash
internetDev="eth0" # 互联网接口
privateNetDev="switch0" # 私有网络接口

uploadRate="20mbit" # 互联网上行带宽
uploadP2PRate="1mbit" # P2P上行带宽
uploadEmerRate=${uploadRate} # 最低延迟上行带宽
uploadPrioRate=${uploadRate} # 优先传送上行带宽
uploadNormalRate="10mbit" # 普通流量上行带宽

downloadRate="100mbit" # 互联网下行带宽
downloadP2PRate="80mbit" # P2P下行带宽
downloadEmerRate=${downloadRate} # 最低延迟传送下行带宽
downloadPrioRate=${downloadRate} # 优先传送下行带宽

# For QoS, MARK 100
iptables -t mangle -F MARK_100 > /dev/null 2>&1
iptables -t mangle -N MARK_100 > /dev/null 2>&1
iptables -t mangle -A MARK_100 -p udp -m length ! --length 0:1350 -j MARK --set-mark 100
while iptables -t mangle -D PREROUTING -i ${privateNetDev} -j MARK_100 >/dev/null 2>&1; do continue; done
iptables -t mangle -I PREROUTING -i ${privateNetDev} -j MARK_100

tc qdisc del dev ${internetDev} root >/dev/null 2>&1
tc qdisc add dev ${internetDev} root handle 1: htb default 50

tc class add dev ${internetDev} parent 1: classid 1:1 htb rate ${uploadRate}
tc class add dev ${internetDev} parent 1:1 classid 1:100 htb rate ${uploadP2PRate} ceil ${uploadP2PRate} prio 7
tc class add dev ${internetDev} parent 1:1 classid 1:10 htb rate ${uploadEmerRate} prio 0
tc class add dev ${internetDev} parent 1:1 classid 1:20 htb rate ${uploadPrioRate} prio 2
tc class add dev ${internetDev} parent 1:1 classid 1:50 htb rate ${uploadNormalRate} prio 5

tc qdisc add dev ${internetDev} parent 1:100 handle 100: sfq perturb 10
tc qdisc add dev ${internetDev} parent 1:10 handle 10: sfq perturb 10
tc qdisc add dev ${internetDev} parent 1:20 handle 20: sfq perturb 10
tc qdisc add dev ${internetDev} parent 1:50 handle 50: sfq perturb 10

# Match TCP SYN
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 1 u32 \
match u8 0x10 0xff at 33 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match TCP ACK Small Packet (< 64 Bytes)
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 1 u32 \
match u8 0x10 0xff at 33 \
match u8 0x06 0xff at 9 \
match u16 0x0000 0xffc0 at 2 \
flowid 1:10

# Match TCP DESTINATION PORT 22
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0016 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match TCP DESTINATION PORT 23
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0017 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match TCP DESTINATION PORT 25
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0019 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match TCP DESTINATION PORT 53
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0035 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match UDP DESTINATION PORT 53
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0035 0xffff at 22 \
match u8 0x11 0xff at 9 \
flowid 1:10

# Match TCP DESTINATION PORT 80
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0050 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match TCP DESTINATION PORT 443
tc filter add dev ${internetDev} protocol ip parent 1: pref 10 u32 \
match u16 0x01bb 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match UDP DESTINATION PORT 443 (QUIC Protocol)
tc filter add dev ${internetDev} protocol ip parent 1: pref 10 u32 \
match u16 0x01bb 0xffff at 22 \
match u8 0x11 0xff at 9 \
flowid 1:20

# Match TCP DESTINATION PORT 465
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x01d1 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match TCP DESTINATION PORT 993
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x03e1 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match TCP DESTINATION PORT 3306
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0cea 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match TCP DESTINATION PORT 3389
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0d3d 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match UDP DESTINATION PORT 3389
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0d3d 0xffff at 22 \
match u8 0x11 0xff at 9 \
flowid 1:10

# Match UDP DESTINATION PORT 8000 (QQ Port)
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x1f40 0xffff at 22 \
match u8 0x11 0xff at 9 \
flowid 1:10

# Match TCP DESTINATION PORT 8080 (WeChat Port)
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x1f90 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match TCP DESTINATION PORT 9418 (GIT)
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x24ca 0xffff at 22 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Limit UDP Huge Packet Rate
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 20 u32 \
match mark 0x0064 0x00ff \
flowid 1:100



# For QoS, MARK 200
iptables -t mangle -F PRIVATE_NET_MARK_200 >/dev/null 2>&1
iptables -t mangle -N PRIVATE_NET_MARK_200 >/dev/null 2>&1
iptables -t mangle -A PRIVATE_NET_MARK_200 -p tcp -j CONNMARK --restore-mark
iptables -t mangle -A PRIVATE_NET_MARK_200 -m mark --mark 200 -j RETURN
iptables -t mangle -A PRIVATE_NET_MARK_200 -m connlimit --connlimit-above 4 --connlimit-mask 24 --connlimit-saddr -m hashlimit --hashlimit-above 512kb/second --hashlimit-burst 5mb --hashlimit-mode srcip,dstip --hashlimit-dstmask 24 --hashlimit-srcmask 32 --hashlimit-name packetRateLimit -j MARK --set-mark 200
iptables -t mangle -A PRIVATE_NET_MARK_200 -j CONNMARK --save-mark
while iptables -t mangle -D POSTROUTING -o ${privateNetDev} -j PRIVATE_NET_MARK_200 >/dev/null 2>&1; do continue; done
iptables -t mangle -I POSTROUTING -o ${privateNetDev} -j PRIVATE_NET_MARK_200

tc qdisc del dev ${privateNetDev} root >/dev/null 2>&1
tc qdisc add dev ${privateNetDev} root handle 1: htb default 100

tc class add dev ${privateNetDev} parent 1: classid 1:1 htb rate ${downloadRate}
tc class add dev ${privateNetDev} parent 1:1 classid 1:100 htb rate ${downloadP2PRate} ceil ${downloadP2PRate} prio 7
tc class add dev ${privateNetDev} parent 1:1 classid 1:10 htb rate ${downloadEmerRate} prio 0
tc class add dev ${privateNetDev} parent 1:1 classid 1:20 htb rate ${downloadPrioRate} prio 2

tc qdisc add dev ${privateNetDev} parent 1:100 handle 100: sfq perturb 10
tc qdisc add dev ${privateNetDev} parent 1:10 handle 10: sfq perturb 10
tc qdisc add dev ${privateNetDev} parent 1:20 handle 20: sfq perturb 10


# Match TCP SYN ACK
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 1 u32 \
match u8 0x12 0xff at 33 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match TCP ACK Small Packet (< 64 Bytes)
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 1 u32 \
match u8 0x10 0xff at 33 \
match u8 0x06 0xff at 9 \
match u16 0x0000 0xffc0 at 2 \
flowid 1:10

# Match multip thread download
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 5 u32 \
match mark 0x00c8 0x00ff \
flowid 1:100

# Match TCP SOURCE PORT 22
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0016 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match TCP SOURCE PORT 23
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0017 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match TCP SOURCE PORT 25
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0019 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match TCP SOURCE PORT 53
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0035 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match UDP SOURCE PORT 53
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0035 0xffff at 20 \
match u8 0x11 0xff at 9 \
flowid 1:10

# Match TCP SOURCE PORT 80
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0050 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match TCP SOURCE PORT 443
tc filter add dev ${privateNetDev} protocol ip parent 1: pref 10 u32 \
match u16 0x01bb 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match UDP SOURCE PORT 443 (QUIC Protocol)
tc filter add dev ${privateNetDev} protocol ip parent 1: pref 10 u32 \
match u16 0x01bb 0xffff at 20 \
match u8 0x11 0xff at 9 \
flowid 1:20

# Match TCP SOURCE PORT 465
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x01d1 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match TCP SOURCE PORT 993
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x03e1 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match TCP SOURCE PORT 3306
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0cea 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:20

# Match TCP SOURCE PORT 3389
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0d3d 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match UDP SOURCE PORT 3389
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x0d3d 0xffff at 20 \
match u8 0x11 0xff at 9 \
flowid 1:10

# Match UDP SOURCE PORT 8000 (QQ Port)
tc filter add dev ${internetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x1f40 0xffff at 20 \
match u8 0x11 0xff at 9 \
flowid 1:10

# Match TCP SOURCE PORT 8080 (WeChat Port)
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x1f90 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:10

# Match TCP SOURCE PORT 9418 (GIT)
tc filter add dev ${privateNetDev} protocol ip parent 1:0 pref 10 u32 \
match u16 0x24ca 0xffff at 20 \
match u8 0x06 0xff at 9 \
flowid 1:20

总结

对P2P的归类效果还是非常好的:

1:1是总的下行,1:100是P2P的类别的下行。

P2P上行也很精确地限制在了1 Mbps:

要对迅雷的全网HTTP资源多线程下载完全正确归类可能做不到,但IP对IP流量大,高数据包速率的都能受限,正常的网页流量,交互式应用受到的影响不会太大。

下行速率的控制会不太准确,毕竟我们无法控制远程计算机给我们发包的速率,需要根据实际情况适当减少rate的值。

对于延迟,就比较难保证了,这依赖于ISP的队列规则,即使我们尽量做到不让ISP处丢包,但大量的数据包排队还是会导致其余应用的延迟增加。我这里把P2P的下行速率限制为80 Mbps,P2P全速下载时,延迟也只是增加了5 – 10 ms,可以接受,ISP处的队列比较合理。

102 Posts

自信、努力、活出精彩;以前未所见的颜色,绘大千世界!
View all posts

Leave a reply

Your email address will not be published. Required fields are marked *