V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
levenwindy
V2EX  ›  Linux

tc + iptables 上行限速失败

  •  
  •   levenwindy · 2023-05-06 05:30:46 +08:00 · 2732 次点击
    这是一个创建于 568 天前的主题,其中的信息可能已经有所发展或是发生改变。

    想要在 Linux 实现,定时限速 Qos 。这几天一直在头疼这问题。 环境:OpenWrt 、Ubuntu 、tc 、iptables

    想法:仅用 iptables 只能对包限制,限速并不理想,所以希望能够用 tc + iptables 实现类似爱快的 Qos 。

    爱快 Qos

    或者有更加优雅的 Qos 方法?要是用 crontab +脚本 来实现,感觉就不太「完美」,。

    1 、仅用 tc 上行 下载限制均 成功

    # 下行限速 
    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
    

    2 、tc + iptables 下行限速 成功

    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
    

    tc + iptables 下行限速 成功

    3 、tc + iptables 上行限速 失败

    # 加载 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
    

    tc + iptables 上行限速 失败

    尝试 PREROUTING POSTROUTING -s -d 都无效,但是看到网上又有成功的案例?

    11 条回复    2023-05-08 19:27:58 +08:00
    levenwindy
        1
    levenwindy  
    OP
       2023-05-06 06:06:56 +08:00
    levenwindy
        2
    levenwindy  
    OP
       2023-05-06 06:11:06 +08:00
    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
    datocp
        3
    datocp  
       2023-05-06 07:08:47 +08:00 via Android   ❤️ 1
    Openwrt 下
    下行 br-lan
    上行 pppoe-wan/eth.xx

    至于为什么需要用 ifb ,天啊我竟然忘光了,从来不用这种接口,原因很简单需要消耗 cpu 自然对流量呑吐有影响,等在电脑前再发。。。
    levenwindy
        4
    levenwindy  
    OP
       2023-05-06 08:02:18 +08:00
    @datocp 晕,之前一直在旁路由测试,又参考了 GLinet 的 gl_eqos ,忘记主路由有 pppoe-wan 这个接口了.......感谢提醒!!

    主路由已经「完美」 tc + iptables 限速!

    测试了一下,单臂旁路由的下行 br-lan 可以用 tc + iptables ,上行只能靠 ifb 接管 eth0 实现,且 iptables 不能完成。估计旁路由得多网口的才能行吧
    datocp
        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 日星期四
    - 生成文档
    levenwindy
        6
    levenwindy  
    OP
       2023-05-06 10:23:45 +08:00
    @datocp 学习了,赞👍

    openwrt 的 tc sfq 会报错,RTNETLINK answers: No such file or directory ,不知道是不是内核缺了啥。
    datocp
        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
    levenwindy
        8
    levenwindy  
    OP
       2023-05-08 06:36:20 +08:00
    @datocp
    还有个问题
    目前安卓只支持 SLACC ,地址不能固定。
    打算做个脚本,根据 IPv4 的地址,判断出目标设备的 IPv6 地址,从而也能对 IPv6 的设备进行限速。

    采取 tc + ip6tables 。这个想法靠谱吗
    datocp
        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
    levenwindy
        10
    levenwindy  
    OP
       2023-05-08 16:25:01 +08:00
    @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 优先级不是应该越高吗?
    datocp
        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 的存在。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1007 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 20ms · UTC 23:09 · PVG 07:09 · LAX 15:09 · JFK 18:09
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.