第一章环境规划与内核调优1.1 网络拓扑假设我们的Linux服务器有三张网卡eth0 (外网)连接光猫IP192.168.1.100/24网关192.168.1.1。eth1 (内网)连接核心交换机IP10.0.0.1/24。eth2 (管理口)可选用于SSH管理。我们的目标是内网用户10.0.0.0/24通过Linux网关上网并对特定IP进行带宽保障。1.2 开启内核转发与优化Linux默认不转发IP包需要手动开启。# 临时开启 echo 1 /proc/sys/net/ipv4/ip_forward # 永久生效写入 sysctl.conf cat /etc/sysctl.conf EOF net.ipv4.ip_forward 1 # 防止 SYN 攻击优化队列 net.core.netdev_max_backlog 5000 net.ipv4.tcp_syncookies 1 EOF sysctl -p1.3 配置NAT网络地址转换使用iptables让内网机器共享外网IP上网。这是网关的核心功能。# 清除旧规则 iptables -F iptables -t nat -F # 设置默认策略 iptables -P INPUT DROP iptables -P FORWARD DROP # 允许内网转发和已建立的连接 iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A FORWARD -s 10.0.0.0/24 -j ACCEPT # 关键SNAT源地址转换伪装成外网IP出去 iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE大神注解这里使用MASQUERADE伪装而非SNAT是因为外网IP可能变动PPPoE拨号MASQUERADE会自动获取出口IP更加灵活。第二章流量整形基石——理解TC与队列规则流量整形并非“限速”那么简单。粗暴地把带宽砍半会导致网络波动。我们需要优先级控制。2.1 为什么选HTBLinux TC支持多种算法如pfifo_fast先进先出、SFQ随机公平队列。但在企业环境HTB是首选。原理类似于银行金库总带宽。根是总金库子类Subclass是不同部门或用户的账户。我们可以给总经理开设“VIP窗口”保证带宽给下载部门开设“普通窗口”限制带宽。概念区分ceiling上限最高能借多少带宽。rate承诺速率最低保证多少带宽。2.2 清除旧规则热身在开始写脚本前先确保网卡干净。# 查看当前 qdisc tc qdisc show dev eth1 # 删除如果有旧规则 tc qdisc del dev eth1 root第三章实战脚本编写——让带宽“听话”这是一个完整的Shell脚本我们命名为/usr/local/bin/bandwidth_control.sh。3.1 定义变量适配不同环境#!/bin/bash # 出口网卡内网 DEVeth1 # 总带宽单位kbit假设下行100Mbps上行20Mbps我们控制下行即内网口 # 注意家庭宽带上下行不对等这里是控制从外网进来的流量所以限制eth1的入口更准确但此处为了演示针对内网用户的下行限速我们控制eth1的出口即发往内网的数据。 # 换算100Mbps 102400 kbit TOTAL_RATE102400 TOTAL_CEIL102400 # 定义具体IP与分组 # 1: 总经理办公室VIP - 保证80M借满100M CEO_IP10.0.0.88 CEO_RATE81920 # 80M CEO_CEIL102400 # 100M # 2: 研发部高优先级 DEV_IP10.0.0.99 DEV_RATE20480 # 20M DEV_CEIL51200 # 50M # 3: 其他所有人共享剩余且优先级最低 OTHER_RATE10240 # 10M OTHER_CEIL40960 # 40M3.2 建立HTB根队列与主干类# 1. 绑定根队列使用 htb tc qdisc add dev $DEV root handle 1: htb default 30 # 2. 建立主干类根类 # classid 1:1这是总带宽池 tc class add dev $DEV parent 1: classid 1:1 htb rate ${TOTAL_RATE}kbit ceil ${TOTAL_CEIL}kbit # 3. 建立子类 # 注意prio 数值越小优先级越高 # 子类 ID: 1:10 (VIP) tc class add dev $DEV parent 1:1 classid 1:10 htb rate ${CEO_RATE}kbit ceil ${CEO_CEIL}kbit prio 0 # 子类 ID: 1:20 (研发) tc class add dev $DEV parent 1:1 classid 1:20 htb rate ${DEV_RATE}kbit ceil ${DEV_CEIL}kbit prio 1 # 子类 ID: 1:30 (默认/其他人) tc class add dev $DEV parent 1:1 classid 1:30 htb rate ${OTHER_RATE}kbit ceil ${OTHER_CEIL}kbit prio 23.3 附加队列规则SFQ保证小包不排队如果仅仅有HTB同一类里的数据流依旧可能发生“大文件抢占小包”的问题。我们需要在每个叶子类上附加sfq随机公平队列或fq_codel控制延迟。# 为每个叶子类挂载队列规则 tc qdisc add dev $DEV parent 1:10 handle 10: sfq perturb 10 tc qdisc add dev $DEV parent 1:20 handle 20: sfq perturb 10 tc qdisc add dev $DEV parent 1:30 handle 30: sfq perturb 10perturb 10表示每10秒改变一次哈希算法防止某个连接霸占带宽。3.4 过滤器核心通过iptables标记将IP映射到类我们需要告诉内核哪个IP的数据包应该进入哪个类Classid。这里使用fw防火墙标记过滤器配合iptables的MARK目标。# 1. 添加过滤器根据防火墙标记 (handle) 来匹配 tc filter add dev $DEV parent 1:0 protocol ip prio 1 handle 10 fw classid 1:10 tc filter add dev $DEV parent 1:0 protocol ip prio 1 handle 20 fw classid 1:20 tc filter add dev $DEV parent 1:0 protocol ip prio 1 handle 30 fw classid 1:30 # 2. 使用 iptables 给不同IP的流量打标记mangle表 # 注意数据包流向是 外网-eth0-Linux-eth1-内网。 # 我们控制的是从 eth1 发出去的数据所以在 PREROUTING 或 FORWARD 链打标记。 # 这里使用 FORWARD 链针对源IP打标记因为对于内网口eth1出方向数据包源IP是内网IP # 清除旧标记 iptables -t mangle -F # 总经理 IP 标记为 10 iptables -t mangle -A FORWARD -s ${CEO_IP} -j MARK --set-mark 10 # 研发部 IP 标记为 20 iptables -t mangle -A FORWARD -s ${DEV_IP} -j MARK --set-mark 20 # 其他所有 IP 标记为 30 (这里是取反排除上面两个IP) iptables -t mangle -A FORWARD -s 10.0.0.0/24 -m mark --mark 0 -j MARK --set-mark 30# 1. 添加过滤器根据防火墙标记 (handle) 来匹配 tc filter add dev $DEV parent 1:0 protocol ip prio 1 handle 10 fw classid 1:10 tc filter add dev $DEV parent 1:0 protocol ip prio 1 handle 20 fw classid 1:20 tc filter add dev $DEV parent 1:0 protocol ip prio 1 handle 30 fw classid 1:30 # 2. 使用 iptables 给不同IP的流量打标记mangle表 # 注意数据包流向是 外网-eth0-Linux-eth1-内网。 # 我们控制的是从 eth1 发出去的数据所以在 PREROUTING 或 FORWARD 链打标记。 # 这里使用 FORWARD 链针对源IP打标记因为对于内网口eth1出方向数据包源IP是内网IP # 清除旧标记 iptables -t mangle -F # 总经理 IP 标记为 10 iptables -t mangle -A FORWARD -s ${CEO_IP} -j MARK --set-mark 10 # 研发部 IP 标记为 20 iptables -t mangle -A FORWARD -s ${DEV_IP} -j MARK --set-mark 20 # 其他所有 IP 标记为 30 (这里是取反排除上面两个IP) iptables -t mangle -A FORWARD -s 10.0.0.0/24 -m mark --mark 0 -j MARK --set-mark 30逻辑闭环当总经理上网时数据包经过FORWARD链被打上mark 10经过eth1出去时tc filter看到mark 10将其放入classid 1:10享受80M保底带宽。第四章进阶优化——解决“小包延迟”与“突发流量”4.1 调整队列长度txqueuelen网卡默认队列长度txqueuelen通常是1000。对于高带宽环境建议调整以防缓存过多导致延迟。ifconfig eth1 txqueuelen 5004.2 针对UDP视频/语音的优化视频会议使用UDP如果UDP没有限制会挤占TCP的ACK包导致网页卡顿。在iptables中我们可以用LENGTH模块限制UDP大包但更优雅的做法是结合qdisc的prio。不过最简单的我们可以给UDP包更高的优先级通过修改mark优先级。思路延伸iptables -t mangle -A FORWARD -p udp --dport 53 -j MARK --set-mark 10DNS优先。4.3 验证与调试脚本写完后赋予执行权限并后台运行chmod x /usr/local/bin/bandwidth_control.sh bash /usr/local/bin/bandwidth_control.sh查看规则是否生效tc -s qdisc show dev eth1查看统计看有没有丢包或Overlimitstc -s class show dev eth1查看各类的流量统计压力测试在内网找两台电脑一台10.0.0.99研发开启迅雷下载一台10.0.0.88CEO使用iperf3打流测试带宽。通过nload或iftop观察流量分配情况。第五章落地为服务——开机自启与异常监控手动运行脚本不够专业我们需要将其变为Systemd服务。创建服务文件/etc/systemd/system/traffic_control.service[Unit] DescriptionBandwidth Control Service Afternetwork.target [Service] Typeoneshot RemainAfterExityes ExecStart/usr/local/bin/bandwidth_control.sh ExecStop/sbin/tc qdisc del dev eth1 root Userroot [Install] WantedBymulti-user.target启动并设置开机自启systemctl daemon-reload systemctl enable traffic_control.service systemctl start traffic_control.service第六章避坑指南大神忠告关于上行与下行本文主要控制下行流量针对内网口eth1的出口。如果要控制上行如限制内网上传视频到云端需要控制外网口eth0的出口脚本逻辑同理但需要调整网卡和IP方向此时源IP变成了外网公网IP标记需要改成-d目标IP匹配比较复杂。一般企业更关注下行。网卡Offload问题部分高性能网卡如Intel i350开启了GRO/GSO会导致TC无法识别小包产生Overlimit超限计数异常。建议关闭ethtool -K eth1 gro off gso off tso off千万不要在虚拟机上测试VMware/VirtualBox的虚拟网卡驱动对TC支持不友好结果不具备参考意义请在物理机或云宿主机直接支持SR-IOV上测试。写在最后很多人觉得Linux只能做服务器实际上在网络领域Linux内核的数据包处理能力远超同价位的硬件路由器。通过本文的HTB iptables组合你已经可以构建一个媲美中端企业级路由器的流控系统。但这只是开始如果你感兴趣下一步可以研究Policy Routing策略路由双线接入电信走电信联通走联通。Netfilter conntrack连接跟踪优化防止NAT表爆满。如果你在部署中遇到RTNETLINK answers: No such file or directory等报错请检查网卡名称是否正确或modprobe sch_htb是否加载了HTB内核模块。希望这篇实战能帮你搞定老板的“网速慢”抱怨。欢迎在评论区留下你的部署心得或踩坑记录。