使用 tc 和 CAKE 进行流量整形

使用 tc 和 CAKE 进行流量整形

将 Ubuntu 机器用作路由器时,流量整形可以让低延迟流量,例如 IP 语音(VoIP)、视频通话和游戏,优先于大批量传输。本指南介绍如何使用 tc(traffic control,流量控制)及 Common Applications Kept Enhanced(CAKE)队列规则配置流量整形。

什么是缓冲区膨胀?

缓冲区膨胀(bufferbloat)发生在网络缓冲区过满时,会导致高延迟和抖动。路由器和调制解调器将多余的数据包排队,而不是丢弃它们,延缓了对时间敏感的流量。CAKE 通过公平排队和主动队列管理来解决这个问题。

先决条件

  • 一台充当路由器的 Ubuntu 系统,具有 WAN 接口,例如 ext0。
  • iproute2 软件包,默认已安装。
  • 使用 systemd-networkd 配置网络。

启用显式拥塞通知(ECN)

ECN 让路由器通过标记数据包而不是丢弃数据包来通知拥塞。与 CAKE 结合使用时,可以改善性能。

创建 /etc/sysctl.d/50-trafficshaping.conf,写入:

# Enable explicit congestion notification for TCP (covers IPv4 and IPv6)
net.ipv4.tcp_ecn = 1

应用设置:

sudo sysctl --system

创建 IFB 接口

tc 流量整形仅作用于出站(egress)流量。要处理入站(ingress)流量,应将其重定向到 Intermediate Functional Block(IFB)接口,使它转变为可以整形的出站流量。

使用 systemd-networkd 创建 IFB 接口。先创建 /etc/systemd/network/20-wanifb.netdev,加入:

[NetDev]
Kind=ifb
Name=wanifb
MTUBytes=1492

创建网络配置文件 /etc/systemd/network/30-wanifb.network:

[Match]
Name=wanifb

[Link]
ActivationPolicy=always-up

重新加载 networkd 以创建接口:

sudo networkctl reload

确认接口存在:

ip link show wanifb

创建流量整形脚本

创建 /usr/local/bin/traffic-shaping,内容如下:

#!/usr/bin/env python3
"""
Traffic shaping using CAKE qdisc.

Shapes both upload (egress) and download (ingress via IFB redirect) traffic
to reduce bufferbloat and improve latency for interactive applications.

Test results at: https://www.waveform.com/tools/bufferbloat
"""

import subprocess
import argparse
import shlex

# Configuration - adjust these values for your connection
CONFIG = {
    "wan_iface": "ext0",        # Physical WAN interface
    "ifb_iface": "wanifb",      # IFB interface for download shaping
    # set to ~90-95% of what your ISP effectively delivers (not just their promise)
    # please measure your bandwidth, and remember it can have time-of-day dependent variations
    "upload_speed": "30mbit",   # you -> WAN
    "download_speed": "80mbit", # WAN -> you
}


def run(cmd, ignore_errors=False):
    """Run a shell command."""
    print(f"  {cmd}")
    subprocess.run(shlex.split(cmd), check=(not ignore_errors))


def clear_all():
    """Remove existing traffic shaping rules."""
    run(f"tc qdisc del dev {CONFIG['wan_iface']} root", ignore_errors=True)
    run(f"tc qdisc del dev {CONFIG['wan_iface']} ingress", ignore_errors=True)
    run(f"tc qdisc del dev {CONFIG['ifb_iface']} root", ignore_errors=True)


def start():
    """Apply traffic shaping rules."""
    print("Clearing existing rules...")
    clear_all()

    # Ensure IFB interface is up
    run(f"ip link set dev {CONFIG['ifb_iface']} up", ignore_errors=True)

    # Shape upload (egress on WAN interface)
    print(f"Setting upload limit to {CONFIG['upload_speed']}...")
    run(f"tc qdisc add dev {CONFIG['wan_iface']} root cake "
        f"bandwidth {CONFIG['upload_speed']} nat")

    # Redirect ingress traffic to IFB interface
    print("Redirecting ingress traffic to IFB interface...")
    run(f"tc qdisc add dev {CONFIG['wan_iface']} handle ffff: ingress")
    run(f"tc filter add dev {CONFIG['wan_iface']} parent ffff: protocol all "
        f"u32 match u32 0 0 action mirred egress redirect dev {CONFIG['ifb_iface']}")

    # Shape download (egress on IFB interface)
    print(f"Setting download limit to {CONFIG['download_speed']}...")
    run(f"tc qdisc add dev {CONFIG['ifb_iface']} root cake "
        f"bandwidth {CONFIG['download_speed']} wash")

    print("Traffic shaping enabled.")


def stop():
    """Remove traffic shaping rules."""
    print("Removing traffic shaping rules...")
    clear_all()
    print("Traffic shaping disabled.")


def status():
    """Show current traffic shaping statistics."""
    print(f"\n=== Upload ({CONFIG['wan_iface']}) ===")
    run(f"tc -s qdisc show dev {CONFIG['wan_iface']}")
    print(f"\n=== Download ({CONFIG['ifb_iface']}) ===")
    run(f"tc -s qdisc show dev {CONFIG['ifb_iface']}")


def main():
    parser = argparse.ArgumentParser(
        description="Manage traffic shaping with CAKE qdisc"
    )
    parser.add_argument(
        "action",
        choices=["start", "stop", "status"],
        help="Action to perform"
    )
    args = parser.parse_args()

    actions = {"start": start, "stop": stop, "status": status}
    actions[args.action]()


if __name__ == "__main__":
    main()

为脚本设置可执行权限:

sudo chmod +x /usr/local/bin/traffic-shaping

编辑 CONFIG,使其符合实际环境:

  • wan_iface:WAN 接口名称,例如 eth0、enp1s0、ppp0;也可以使用 systemd.link(5) 将接口重命名为 ext0。
  • ifb_iface:IFB 接口名称,必须与 netdev 文件一致。
  • upload_speed:设置为实际上行速率的90%–95%。
  • download_speed:设置为实际下行速率的90%–95%。

创建 systemd 服务

创建 /etc/systemd/system/traffic-shaping.service:

[Unit]
Description=Traffic shaping with CAKE
After=network-online.target
Wants=network-online.target
# Bind to the WAN and IFB interfaces
BindsTo=sys-subsystem-net-devices-ext0.device
After=sys-subsystem-net-devices-ext0.device
BindsTo=sys-subsystem-net-devices-wanifb.device
After=sys-subsystem-net-devices-wanifb.device

[Service]
Type=oneshot
ExecStart=/usr/local/bin/traffic-shaping start
ExecStop=/usr/local/bin/traffic-shaping stop
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

启用并启动服务:

sudo systemctl daemon-reload
sudo systemctl enable --now traffic-shaping.service

验证配置

检查服务是否运行:

sudo systemctl status traffic-shaping.service

查看流量整形统计:

sudo /usr/local/bin/traffic-shaping status

输出展示上传和下载队列的数据包数、传输字节数及丢包统计。

测试缓冲区膨胀

使用在线测试检查配置:

  1. 访问 Waveform Bufferbloat Test。
  2. 运行测试,观察上传和下载期间的延迟。
  3. 评级 A 或 B 表明缓冲区膨胀控制良好。

没有流量整形时,测速期间延迟通常会升到数百毫秒。原文说明,正确配置 CAKE 后,即使有负载,延迟也应保持较低水平,增加量低于30毫秒。

CAKE 选项说明

脚本使用以下 CAKE 选项:

  • bandwidth:目标带宽上限。
  • nat:路由器执行 NAT 时,启用 NAT 查找,以正确识别流,脚本用于上传。
  • wash:清除其他设备可能错误设置的 DSCP 标记,脚本用于下载。

其他可能有用的选项:

  • diffserv4:根据 DSCP 标记启用四级优先级。
  • dual-srchost / dual-dsthost:让各主机更公平地共享带宽。
  • docsis:针对有线调制解调器连接优化。

完整选项见 tc-cake(8) 手册。

故障排查

如果流量整形未按预期工作:

  1. 确认接口存在:

    ip link show ext0
    ip link show wanifb
  2. 检查服务错误:

    sudo journalctl -eu traffic-shaping.service
  3. 确认已加载 CAKE 模块:

    lsmod | grep cake
  4. 手动测试:

    sudo /usr/local/bin/traffic-shaping stop
    sudo /usr/local/bin/traffic-shaping start

延伸阅读

原文:Traffic shaping with tc and CAKE,Ubuntu Server 文档、Canonical 与贡献者。中文翻译;代码、测试说明及预期结果来自原文,相关版权与许可归原权利人。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容