想要在 Linux 实现,定时限速 Qos 。这几天一直在头疼这问题。 环境:OpenWrt 、Ubuntu 、tc 、iptables
想法:仅用 iptables 只能对包限制,限速并不理想,所以希望能够用 tc + iptables 实现类似爱快的 Qos 。
或者有更加优雅的 Qos 方法?要是用 crontab +脚本 来实现,感觉就不太「完美」,。
# 下行限速
tc qdisc add dev br-lan root handle 1: htb default 10
tc class add dev br-lan parent 1:0 classid 1:1 htb rate 1000mbit
tc class add dev br-lan parent 1:1 classid 1:11 htb rate 20mbit ceil 50mbit
tc class add dev br-lan parent 1:1 classid 1:12 htb rate 35mbit ceil 100mbit
tc filter add dev br-lan protocol ip parent 1:0 prio 0 u32 match ip dst 192.168.1.170/32 flowid 1:11
#################################################################
# 上行限速
# 加载 ifb 驱动并创建 ifb 网卡
ip link add dev br-lan-ifb name br-lan-ifb type ifb
ip link set dev br-lan-ifb up
# 将 br-lan 流量全部重定向到 br-lan-ifb 处理
tc qdisc add dev br-lan ingress
tc filter add dev br-lan parent ffff: protocol ip u32 match u32 0 0 flowid 1:1 action mirred egress redirect dev br-lan-ifb
tc qdisc add dev br-lan-ifb root handle 1: htb default 10
tc class add dev br-lan-ifb parent 1:0 classid 1:1 htb rate 30mbit
tc class add dev br-lan-ifb parent 1:1 classid 1:11 htb rate 1mbit ceil 2mbit
tc class add dev br-lan-ifb parent 1:1 classid 1:12 htb rate 3mbit ceil 4mbit
tc filter add dev br-lan-ifb protocol ip parent 1:0 prio 0 u32 match ip src 192.168.1.170/32 flowid 1:12
tc qdisc del dev br-lan root 2>/dev/null
tc qdisc del dev br-lan ingress 2>/dev/null
tc qdisc del dev br-lan-ifb root 2>/dev/null
ip link del dev br-lan-ifb 2>/dev/null
iptables -t mangle -F POSTROUTING
iptables -t mangle -F PREROUTING
iptables -t mangle -L
tc qdisc add dev br-lan root handle 1: htb default 10
tc class add dev br-lan parent 1:0 classid 1:1 htb rate 1000mbit
tc class add dev br-lan parent 1:1 classid 1:11 htb rate 20mbit ceil 50mbit
tc class add dev br-lan parent 1:1 classid 1:12 htb rate 35mbit ceil 100mbit
# 替换 tc filter add dev br-lan protocol ip parent 1:0 prio 0 u32 match ip dst 192.168.1.170/32 flowid 1:11
tc filter add dev br-lan parent 1:0 protocol ip prio 0 handle 1011 fw classid 1:11
iptables -t mangle -A POSTROUTING -d 192.168.1.170 -j MARK --set-xmark 1011
iptables -t mangle -A POSTROUTING -d 192.168.1.170 -j RETURN
# 加载 ifb 驱动并创建 ifb 网卡
modprobe ifb numifbs=1
# 加载 ifb 驱动并创建 ifb 网卡
ip link add dev br-lan-ifb name br-lan-ifb type ifb
ip link set dev br-lan-ifb up
# 将 br-lan 流量全部重定向到 br-lan-ifb 处理
tc qdisc add dev br-lan ingress
tc filter add dev br-lan parent ffff: protocol ip u32 match u32 0 0 flowid 1:1 action mirred egress redirect dev br-lan-ifb
tc qdisc add dev br-lan-ifb root handle 1: htb default 10
tc class add dev br-lan-ifb parent 1:0 classid 1:1 htb rate 30mbit
tc class add dev br-lan-ifb parent 1:1 classid 1:11 htb rate 1mbit ceil 2mbit
tc class add dev br-lan-ifb parent 1:1 classid 1:12 htb rate 3mbit ceil 4mbit
#替换 tc filter add dev br-lan-ifb protocol ip parent 1:0 prio 0 u32 match ip src 192.168.1.170/32 flowid 1:12
tc filter add dev br-lan-ifb parent 1:0 protocol ip prio 1 handle 1021 fw classid 1:12
# 失败
iptables -t mangle -A PREROUTING -s 192.168.1.170 -j MARK --set-xmark 1011
iptables -t mangle -A PREROUTING -s 192.168.1.170 -j RETURN
尝试 PREROUTING POSTROUTING -s -d 都无效,但是看到网上又有成功的案例?
1
levenwindy OP |
2
levenwindy OP 3 、这里打错了,--set-xmark 1021
# 失败 iptables -t mangle -A PREROUTING -s 192.168.1.170 -j MARK --set-xmark 1021 iptables -t mangle -A PREROUTING -s 192.168.1.170 -j RETURN |
3
datocp 2023-05-06 07:08:47 +08:00 via Android 1
Openwrt 下
下行 br-lan 上行 pppoe-wan/eth.xx 至于为什么需要用 ifb ,天啊我竟然忘光了,从来不用这种接口,原因很简单需要消耗 cpu 自然对流量呑吐有影响,等在电脑前再发。。。 |
4
levenwindy OP @datocp 晕,之前一直在旁路由测试,又参考了 GLinet 的 gl_eqos ,忘记主路由有 pppoe-wan 这个接口了.......感谢提醒!!
主路由已经「完美」 tc + iptables 限速! 测试了一下,单臂旁路由的下行 br-lan 可以用 tc + iptables ,上行只能靠 ifb 接管 eth0 实现,且 iptables 不能完成。估计旁路由得多网口的才能行吧 |
5
datocp 2023-05-06 08:32:14 +08:00 1
没玩过单臂,不知道 ifconfig 输出是什么。一般的说法是 qos 是作用在上行方向才有机会控制包,单臂应该也是存在上行的接口。但是它是处在路由的下级,一般也就起一个限速的作用。所以还是尽可能的直接在出口路由实现 qos ,出口的路由一般不能用 mac 地址但是还能用各个 vlan 的 ip 地址来区分。如果用基于访问目的端口的 qos ,而不是源 ip 就没这些问题。
ifb 这种并未应用在真实环境,好多年了,都不知道验证的是什么问题。。。 ---------------- 这几天在 google 关键字 linux imq ifb 。IFB ( Intermediate Functional Block )是 IMQ ( InterMediate Queuing )的替代者。linux 中的流量控制都是针对输出方向的,而输入方向的流量限制只有一个队列规则 ingress qdisc 。系统通过 ingress qdisc 把输入方向的数据包重定向到虚拟设备 IFB ,在 IFB 的输出方向配置多种 qdisc ,就可以达到对输入方向的流量做队列调度的目的。IFB 和 IMQ 最显著的差别就是不再和 netfilter 产生联动关系。有利也有蔽,今天想在 wan 做 ingress 控制本地 lan 接口流量,就因为 DNAT 问题无果。注意 openwrt 下的 ifb 打过补丁支持 conntrack 状态。 On 7/19/06, Andy Furniss <lists at andyfurniss.entadsl.com> wrote: > Rajesh Mahajan wrote: > > Is IFB realy replacement of IMQ > > Mostly - it hooks before/after netfilter though, so if you really need > IMQ to hook "in" netfilter (eg. to get denatted addresses on ingress so > you can seperate INPUT and FORWARD traffic), you still need IMQ. > > Andy. IFB 还有一个比较神奇的特性,它可以只在虚拟 ifb 接口做 tc 流量控制,而将其它接口的流量都重定向到虚拟 ifb 接口统一做流量控制。 以前在一个路由上做了 1LAN+3VLAN 接口,其中有个 VLAN 有 9 个使用静态路由实现的网段,总计 13 个 IP 段。为了实现动态限速,用脚本对 13 个网段的 ip 进行 interface 接口判断处理,然后插值到特定 interface 接口实现动态限速过程。而下面的实例实现 2 个 interface 接口变为 1 个虚拟的 ifb 接口,而且可以真正的用一个统一的 ifb 虚拟接口通过 prio 对不同接口的流量实现优先级控制。这个实例的延伸第一次将应用于家用环境的强调延迟效果的 QOS 脚本和应用于公司环境基于 IP 管理的 QOS 实现通过 ifb 揉和在一起。高优先级的流量永远保持高优先级,其它流量基于 IP 实现动态限速,HOHO 。。。开心。 在 wan ingress 接口,由于它在 DNAT 之前,只能获得 wan ip 而不能获得本地 lan ip ,不能实现基于本地接口的统一限速。可以通过重定向每个本地接口的流量到虚拟接口 ifb ,实现针对不同接口的流量限制。 #!/bin/sh -x TC=$(which tc) DOWNLINK=570 DDEV=ifb0 #rmmod ifb insmod ifb numifbs=1 ip link set dev $DDEV txqueuelen 128 up $TC qdisc del dev $DDEV root 2>/dev/null $TC qdisc add dev $DDEV root handle 1: htb default 10 r2q 10 $TC class add dev $DDEV parent 1: classid 1:1 htb rate 1000mbit burst 1000k $TC class add dev $DDEV parent 1:1 classid 1:100 htb rate 1000mbit ceil 1000mbit burst 1000k prio 0 $TC class add dev $DDEV parent 1: classid 1:2 htb rate $((DOWNLINK))kbps ceil $((DOWNLINK))kbps $TC class add dev $DDEV parent 1:2 classid 1:10 htb rate $((DOWNLINK*25/100))kbps ceil $((DOWNLINK*95/100))kbps prio 1 $TC class add dev $DDEV parent 1:2 classid 1:30 htb rate $((DOWNLINK*25/100))kbps ceil $((DOWNLINK*25/100))kbps prio 1 $TC qdisc add dev $DDEV parent 1:100 handle 100: sfq perturb 10 $TC qdisc add dev $DDEV parent 1:10 handle 10: sfq perturb 10 $TC qdisc add dev $DDEV parent 1:30 handle 30: sfq perturb 10 tc qdisc del dev tun0 root ip link set dev tun0 txqueuelen 32 tc qdisc add dev tun0 root handle 1: prio priomap 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 tc filter add dev tun0 parent 1:0 protocol ip priority 10 u32 match u32 0 0 flowid 1: action mirred egress redirect dev $DDEV tc filter add dev ifb0 parent 1:0 prio 9 protocol ip u32 match ip src 192.168.8.0/24 match ip dst 10.1.0.0/24 flowid 1:100 tc filter add dev ifb0 parent 1:0 prio 10 protocol ip u32 match ip dst 10.1.0.0/24 flowid 1:10 #tc filter add dev ifb0 parent 1:0 prio 10 protocol ip u32 match ip dst 10.1.0.7/32 flowid 1:10 tc qdisc del dev eth0.1 root ip link set dev eth0.1 txqueuelen 32 tc qdisc add dev eth0.1 root handle 1: prio priomap 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 tc filter add dev eth0.1 parent 1:0 protocol ip priority 10 u32 match u32 0 0 flowid 1: action mirred egress redirect dev $DDEV tc filter add dev ifb0 parent 1:0 prio 29 protocol ip u32 match ip src 192.168.8.0/24 match ip dst 192.168.8.0/24 flowid 1:100 tc filter add dev ifb0 parent 1:0 prio 30 protocol ip u32 match ip dst 192.168.8.0/24 flowid 1:30 参考文档 https://github.com/westnetz/qos-script/blob/master/qos-htb-sfq.sh ifb By Linux Foundatio... - November 19, 2009 - 10:23am http://www.linuxfoundation.org/collaborate/workgroups/networking/ifb * 2015 年 07 月 31 日星期五 - 增加 1000mbit 环境的实例,避免当路由做为服务器时流量被限制的问题。 - 注意 ifb0 接口 filter 时的 prio 优先级控制。 * 2015 年 07 月 09 日星期四 - 生成文档 |
6
levenwindy OP |
7
datocp 2023-05-06 19:44:40 +08:00 1
qos 相关包 kmod-sched kmod-sched-connmark kmod-sched-core tc kmod-ifb
opkg files kmod-sched|grep sfq /lib/modules/5.4.215/sch_sfq.ko |
8
levenwindy OP @datocp
还有个问题 目前安卓只支持 SLACC ,地址不能固定。 打算做个脚本,根据 IPv4 的地址,判断出目标设备的 IPv6 地址,从而也能对 IPv6 的设备进行限速。 采取 tc + ip6tables 。这个想法靠谱吗 |
9
datocp 2023-05-08 09:18:00 +08:00
目前我在 100mbps 移动专线,大概 250 终端,erx/openwrt 21.02.5 。用的 htb 不是 hfsc 。因为 htb 的 prio 优先级能更好的按预想控制,做到按 prio 优先级的 p2p 流量避让。另外因为不了解 ipv6 ,所以在 openwrt 里主路由是尽可能的关闭了 ipv6 只用 ipv4 ,没有 ipv6 的验。
cat /etc/config/firewall|grep ipv6 option disable_ipv6 '1' option family 'ipv6' option family 'ipv6' option family 'ipv6' option family 'ipv6' cat /etc/config/network|grep ipv6 option ipv6 '0' config interface 'wan6' option proto 'none' option device 'eth1' cat /etc/sysctl.d/10-default.conf|grep ipv6 net.ipv6.conf.default.forwarding=0 net.ipv6.conf.all.forwarding=0 /etc/rc.local while true do if [ $(date +"%Y%m%d") -gt 20200217 ]; then sleep 15 ip -6 addr del ::1/128 dev lo for i in br-lan eth0 eth1 eth2 eth3 eth4 dsa eth0.1 eth0.2 lo tap_soft; do ip -f inet6 addr flush scope link dev $i;done break fi;done exit 0 ebtables -L Bridge table: filter Bridge chain: INPUT, entries: 5, policy: DROP -p IPv4 -j ACCEPT -p ARP -j ACCEPT -p 0x888e -j ACCEPT -p IPv6 -j DROP -j CONTINUE Bridge chain: FORWARD, entries: 4, policy: DROP -p IPv4 -j ACCEPT -p ARP -j ACCEPT -p IPv6 -j DROP -j CONTINUE Bridge chain: OUTPUT, entries: 2, policy: DROP -p IPv4 -j ACCEPT -p ARP -j ACCEPT 另外 qos 不是限速,理想状态是时刻保持 100mbps 的流量全速运行,这才能尽可能的让网络高效使用。一旦进入限速思维完蛋了。而且 iptables 的遍历规则过程也是个严重影响 cpu 性能的过程,不可能在大量终端的环境使用限速这种方法。 下面就是结合了 tomato QOS 的实现,已经应用了很多年了。之前是在 135KB/s 上行的线路实现,如今随着上行的不断增加,更是没什么问题。 核心是配置 tc class 流量分组 1.使用 1:2 抑制非高优先级流量只能使用 90%的上行带宽。 2.根据观察电话线 ADSL 的经验,流量占用当前带宽的 60%所有流量拥有极低的延迟,流量达到 80%延迟还勉强开始有下行掉速的问题。这个经验非常有用,这也是为什么这个 QOS 感觉很无敌的地方。没有其它的乱 78 糟的名词。只需考虑当前流量占用整体流量 60/80 的占比就可以达到非常好的效果。 3.根据 prio 优先级,不同的 dstport 访问优先级就可以实现 p2p 自动避让其它流量。 3.还有些问题,不知道 quantum 和 r2q 300 如何根据 100mbps 算出最佳值。。。QOS 的东西真得是博大精深,每个人的研究体会都不大一样。 # add HTB root qdisc $TC qdisc del dev $UDEV root 2>/dev/null $TC qdisc add dev $UDEV root handle 1: htb default 40 r2q 300 #$TC class add dev $UDEV parent 1: classid 1:1 htb rate 1Gbit ceil 1Gbit $TC class add dev $UDEV parent 1: classid 1:1 htb rate 150Mbit ceil 150Mbit #$TC class add dev $UDEV parent 1:1 classid 1:100 htb quantum 1514 rate $((UPLINK*10/100))kbps ceil 1Gbit prio 5 $TC class add dev $UDEV parent 1:1 classid 1:2 htb rate $((UPLINK*8/10))kbps ceil $((UPLINK*9/10))kbps #$TC class add dev $UDEV parent 1:1 classid 1:10 htb quantum 1514 rate $((UPLINK*1/10))kbps ceil $((UPLINK))kbps prio 0 $TC class add dev $UDEV parent 1:1 classid 1:10 htb rate $((UPLINK*1/10))kbps ceil $((UPLINK))kbps prio 0 $TC class add dev $UDEV parent 1:1 classid 1:20 htb rate $((UPLINK*1/10))kbps ceil $((UPLINK))kbps prio 2 $TC class add dev $UDEV parent 1:2 classid 1:30 htb rate $((UPLINK*3/10))kbps ceil $((UPLINK*90/100))kbps prio 3 $TC class add dev $UDEV parent 1:2 classid 1:40 htb rate $((UPLINK*3/10))kbps ceil $((UPLINK*85/100))kbps prio 4 ##这就是个揉和了 tomato qos 的 openwrt 实现。 iptables -t mangle -S -P PREROUTING ACCEPT -P INPUT ACCEPT -P FORWARD ACCEPT -P OUTPUT ACCEPT -P POSTROUTING ACCEPT -N QOSO -A PREROUTING -i eth1 -j DSCP --set-dscp 0x00 -A PREROUTING -i eth1 -j CONNMARK --restore-mark --nfmask 0xff --ctmask 0xff -A FORWARD -o eth1 -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1400:1536 -j TCPMSS --clamp-mss-to-pmtu -A FORWARD -i eth1 -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1400:1536 -j TCPMSS --clamp-mss-to-pmtu -A FORWARD -o eth1 -j QOSO -A OUTPUT -o eth1 -j QOSO -A QOSO -j CONNMARK --restore-mark --nfmask 0xff --ctmask 0xff -A QOSO -m mark ! --mark 0x0/0xff -j ACCEPT -A QOSO -p udp -m mark --mark 0x0/0xff -m udp --dport 6060 -j MARK --set-xmark 0x10/0xff -A QOSO -p tcp -m mark --mark 0x0/0xff -m multiport --dports 992,1992 -j MARK --set-xmark 0x10/0xff -A QOSO -p udp -m mark --mark 0x0/0xff -m multiport --dports 53,123 -j MARK --set-xmark 0x20/0xff -A QOSO -p tcp -m mark --mark 0x0/0xff -m multiport --dports 22,23,3389 -j MARK --set-xmark 0x20/0xff -A QOSO -p tcp -m mark --mark 0x0/0xff -m multiport --dports 80,443,1080,1863,8080:8081,12000,14000,16285 -j MARK --set-xmark 0x30/0xff -A QOSO -p udp -m mark --mark 0x0/0xff -m multiport --dports 500,1701,4000:4030,4500,5989,8000:8001,16285 -j MARK --set-xmark 0x30/0xff -A QOSO -p tcp -m mark --mark 0x0/0xff -m multiport --dports 20,21,25,143,465,993,1024:65535 -j MARK --set-xmark 0x40/0xff -A QOSO -p udp -m mark --mark 0x0/0xff -m udp --dport 1:65535 -j MARK --set-xmark 0x40/0xff -A QOSO -j CONNMARK --save-mark --nfmask 0xff --ctmask 0xff |
10
levenwindy OP @datocp
100mbps 移动专线,250 终端,这有点狠啊。 PT /PCDN 跑满上行,广东电信就会丢包(游戏、直播等),联通完全不会,光猫(桥接)已关闭 Qos 。不清楚是不是运营商 Qos in OLT ? ------------------------------ rate 1/10 ceil UPLINK prio 0 rate 1/10 ceil UPLINK prio 2 rate 3/10 ceil UPLINK*9/10 prio 3 ------------------------------ 按我的理解,rate 越大,prio 优先级不是应该越高吗? |
11
datocp 2023-05-08 19:27:58 +08:00 via Android
这种属于保障型 qos ,当年主要是想在迅雷下玩 cs 。所以专门设定了一个绝对高优先级的游戏流量分组。按 135kb 这个分组总带宽仍然有 13.5KB 左右,而 cs 的交互流量大概只有 5KB 左右。当时的测试结果 cs 的延迟是低于 19ms 。
而位于 1:40 的未分类流量延迟就很夸张了,接近 600ms 。 Qos 是用于解决流量和延迟的对比关系,在 hfsc 是有关于带宽和流量的计算公式。但是有了这个 class 分组实现,我只关心当前分组的流量低于 80%就可以,无需精确去计算。 至于流量的抢夺仍和并发数有关系的,prio 常见的有感知的就是游戏 /语音,web 浏览仍然优先于其它未分级的流量,做到 p2p 的自动避让。 当然下行方向为了防止有用户大流量下载,仍然是动态将有流量的 ip 绑定到 60%的流量分组但是拥有高优先级,其他 ip 拥有 100%的流量但是次优先级。这样就做到了流量高可用,每个 ip 都有机会抢夺 100%的带宽,无视任何 p2p 的存在。 |