(原文撰写于 2022 年 9 月)

因为 Linux 下的 tc 流量整形只能对出口流量进行配置。若要双向流量限制,只能通过之前的方案:使用一台作为路由的主机当作网关来控制另一台主机,在路由主机上配置 tc 策略进行双向出口 (路由->WAN, 路由->内部主机) 流量整形。

但是这里有一个缺点,那就是很麻烦。除非你想作为一个专门的网管,对内部网络进行配置,然而大多数情况下,其实只需要有一台测试的 Linux 临时配置一下而已。

那么有没有一种很爽的方式来解决这个问题呢?那肯定是有的。

虚拟以太网设备

Linux 内置了创建虚拟以太网设备的支持。通过 ip-link 可以添加虚拟的以太网设备,下列命令可以创建一组虚拟以太网接口:

ip link add dev veth type veth peer name vpeer

veth 和 vpeer 设备互为对端。可以对其分配 ip 地址等正常网络设备的操作。

网络命名空间

同时 Linux 也支持网络命名空间 ip-netns,网络命名空间作为网络堆栈的一个副本。包含自己的路由,防火墙和网络设备。通过配置可用的网络命名空间,就可以在单个主机上配置一个包可以通过两次防火墙!

通过将虚拟以太网设备的 vpeer 端设备移动到网络命名空间中,并且配置正确的 ip 和 默认路由规则。在网络命名空间中就可以访问外部网络:

ip netns add myns # 创建命名空间
ip link set vpeer netns myns # 移动 vpeer 到命名空间中

双向流量控制

目标进程启动在网络命名空间中,然后直接在两个虚拟以太网设备上通过 tc 配置流量规则,则可以实现双向流量控制:网络命名空间中的 vpeer 设备上配置,则意味着是命名空间中的内部到外部的流量进行整形。veth 设备则是配置外部网络到命名空间内部的流量整形。

这样就可以这样的通过双向流量整形来实现模拟下载很慢的情况了(如果只是限制发出的包仅仅只是限制了确认回包。虽然也可以通过这种方式来限制发送端的发送窗口进而影响发送速率,但是不是实际理想的情况),实际的网络状态可能非常好。

成品解决方案

上述所有的配置功能已经编写好一个脚本:https://github.com/frimin/vetc.sh,并已兼容 firewalld / iptables / nftables 防火墙由非命名空间网络 NAT 到命名空间网络中。

参考文章

https://superuser.com/questions/764986/howto-setup-a-veth-virtual-network https://etherarp.net/connecting-network-namespaces-with-veth/index.html https://gist.github.com/dpino/6c0dca1742093346461e11aa8f608a99#file-ns-inet-sh-L64 https://samwho.dev/blog/emulating-bad-networks/ https://github.com/lukaszlach/docker-tc