路由虚拟化,意义何在?
见此题目,心急的路由玩家,可能等不及要说,我要all in one啊。
“主路由还要承担NAS、HA、运行docker,甚至一些通用win/linux虚拟机的脚本工作,我已经按大佬教程在ikuai/openwrt上搞了kvm,挺香,你不搞么?”
非也,我是反向操作,先虚拟化,再跑路由,理由也很简单 –
“首先,专业方案解决专业问题,所以尽管底层都是kvm,我不喜欢用一个路由系统做virtualization host,NAS系统也同理。其次,我要极致灵活性和对特殊需求的支持能力。”
对第二点,我举个简单的例子,我希望ikuai实现以下需求 –
- 对硬件#1物理口做成单臂路由,vlan id 3为WAN去拨号,vlan id 12为LAN,且开DHCP。
- 然后把#2物理口加入ikuai WAN2(第二条宽带,或者4G wwan转成有线接入做冗余连接备用)。
- 再把#3物理口加入LAN,而#3物理口vlan id 22放给二级路由的LLAN。
- 二级路由的WAN不走物理口,直接走host内部虚拟交换机接在ikuai LAN的下边。
如果路由裸装ikuai系统,我不知道是否方便实现,花点心思或许可以吧。但如果物理机是Proxmox VE,我实际遇到的使用场景应该比如上说的还复杂些,完全不是问题,ikuai完全不需要设置任何vlan,如上需求可以都在pve host的linux vlan和bridge的层面做好,进路由看到的完全是多块virtio独立网卡。
“那么今天你要讲,怎么在pve+ikuai上实现这个需求?这种需求一般人用不上,我为何要了解?”
非也,今天想讲下我在家中遇到一个更有意义的需求,给智能电视(尤其某些第三方APP)限速,且主要针对上传速度。
首先,为何要限速?观察下图,黑线两侧,是限速前后状态对比。
我们可以很明确的发现,限速前上传速度是4MBytes/s左右,可以说基本把家宽的上行带宽吃完了,此外我们的RK3399被迅速推高了15℃,温度达到70℃左右,别忘记现在还只是春末夏初。
好说,即使硬件倒退,全家继续使用MTK方案硬路由,带宽打满的前提下,发热量依然控制的非常好,温度那就不是个问题。
但上传带宽呢?
“你上传带宽闲着也是闲着,占了做P2P服务大家,不是更好?否则你以为免费电视APP的流畅度从哪来的?”
嗯,键盘侠高风亮节,你杠你赢,请恕在下小肚鸡肠了。
- 其一,运营商既然给了上传带宽,总是有意义的。且不说家里NAS存储在外访问,即使开个腾讯会议、打个微信视频、甚或孩子上个网课总要使用上行带宽的吧。电视APP一开,其他视频应用都卡,显然不是家里人希望看到的。
- 其二,资源闲置就可以不打招呼全数占用,这个逻辑不难反驳,不需要举一些很low的例子,对吧。
- 其三,我所谓限速,是指某种意义上的对等。根据收益决定付出,我下行峰值500KB/s左右,那么完全乐意提供上行500KB~1MB/s,没毛病吧。
现在看如何实现这一点,既然主路由是OpenWrt,那么当仁不让就是SQM以及胜任底层traffic shaping工作的QoS,前者可以提供图形界面但只能整体接口限速,而后者自定义程度更高,但基本就是命令行使用标准Linux tc来自制根据ip地址的精确流控。关于“既要又要”的实现,还有个插件叫做eqos,前提是编译固件要考虑进去,且据传不能开各种“流量分载”以及“硬件转发”,否则流控失效。
综上,如果不想让iptables简单粗暴直接丢包,而是想实现准确限速的话,其实不管安装什么luci app,底层基本都是Linux tc,没问题,读文档去。太高深的要花心思学习,照猫画虎总不会太难吧。
这是我们当前路由的软硬件原理框图,既然我要过滤来自于Xiaomi TV的上行流量,我觉得位置A,也即OpenWrt的eth0(通常会被打成网桥br-lan,图中未绘制),是首次尝试tc限速的好位置。
由于第一次尝试可耻的失败了,具体脚本就不贴了。甚至于在东改西改之后,居然下行流量得到了限制,而上行流量依然我行我素的打满。继续读资料,才逐渐意识到,对于OpenWrt,其eth0显然是入向接口(ingress)。而tc除了控制丢包,所能提供的所有流量整形功能,都是工作在出向接口(eth1)上的,明白,这个好解决。
接下来就尝试在B点落实流控。
1 2 3 4 5 6 7 8 9 |
# 如果该接口有旧的qdisc,那么先清除旧的 tc qdisc del dev pppoe-wan root # 增加新的 tc qdisc add dev pppoe-wan root handle 1: htb default 30 tc class add dev pppoe-wan parent 1: classid 1:1 htb rate 1000mbit tc class add dev pppoe-wan parent 1:1 classid 1:10 htb rate 3mbit ceil 8mbit tc class add dev pppoe-wan parent 1:1 classid 1:80 htb rate 1000mbit # 查看已有的队列工作情况 tc -s -d class show dev pppoe-wan |
B点看起来是eth1,但实际由于家宽主路由肯定是PPPoE拨号的,所以目标interface在OpenWrt里体现为pppoe-wan。
队列建好,然后把来自于电视(假设ip地址 192.168.19.58)的流量导入队列。
1 2 3 4 5 6 7 8 |
# 以下两种办法导入流量,任选一种即可 # 办法1,iptables直接把流量classify进去 iptables -t mangle -A POSTROUTING -o pppoe-wan -s 192.168.19.58 -j CLASSIFY --set-class 1:10 iptables -t mangle -nvL # 办法2,iptables给流量打标签,然后tc检查包标签来把数据包排入队列 tc filter add dev pppoe-wan parent 1:0 protocol ip prio 10 handle 10 fw flowid 1:10 iptables -t mangle -A PREROUTING -s 192.168.19.58 -j MARK --set-mark 10 |
搞定,去测试。
结果:依然限不住。
这就说不通了,pppoe-wan是egress(出向)接口,所有命令也没有报错,为何限不住?
继续找博文,发现技术应用是没有问题的,而限不住的原因,出在了OpenWrt的software flow offloading上。这种转发加速虽然是纯软件方案,但还是让流量绕过了iptables。虽然同流量转发的cpu占用低了,但限速也一起失效了。有相同效果还有SFE,以及MTK方案的HWNAT等等,都是转发效率极大提高,但所有基于iptables的流量统计和QoS都玩不转了。
关闭“软件流量分载”,立竿见影,限速完美生效。
现在题目从问答变成了选择,要“限速”还是要“加速”?
“成年人不做选择,我全都要。”
然后就开始琢磨图中的C点,Netgear VLAN交换机的QoS管理,菜单设置,一点生效,轻松解决。
可惜,人嘛,始终骗不了自己。我要的是tc,且要控制的是出公网流量,通过硬件在内网限制算什么?换交换机or换接口还要重设,听起来就不好玩。
那么怎么去解决超越iptables失效的问题,参考一些博文,在入向接口eth0上增加ifb么?
“增加ifb其实就是在入向(ingress)接口上加一个伪接口,以便于凭空造出一个出向(egress)接口,再用tc做流量整形。”
嗯,这就有意思了,本来我们的OpenWrt就是运行在armvirt虚拟化层面上,那么上行流量在到达OpenWrt之前……
“D点位置!”
先ping两组ICMP,在vnet0上抓包看,能不能从Host Armbian上拿到进OpenWrt eth0的流量。
1 2 3 4 5 6 7 8 9 10 11 |
$ tcpdump -i vnet0 -n icmp and host 192.168.19.58 tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on vnet0, link-type EN10MB (Ethernet), snapshot length 262144 bytes 17:46:18.522278 IP 192.168.19.58 > 110.242.68.66: ICMP echo request, id 2, seq 1, length 64 17:46:18.533646 IP 110.242.68.66 > 192.168.19.58: ICMP echo reply, id 2, seq 1, length 64 17:46:19.524263 IP 192.168.19.58 > 110.242.68.66: ICMP echo request, id 2, seq 2, length 64 17:46:19.534978 IP 110.242.68.66 > 192.168.19.58: ICMP echo reply, id 2, seq 2, length 64 ^C 4 packets captured 4 packets received by filter 0 packets dropped by kernel |
结果显而易见,上行流量确定经过vnet0。且根据我浅薄的网络基础知识,在Host机上,上行流量应该是end0.12接口进,过bri-lan桥,vnet0出,此后就进入了OpenWrt,这意味着什么呢?
“vnet0是出方向接口,如果把tc放在vnet0上就能完美解决一切问题。”
理论上,只要把刚才一串命令把接口从pppoe-wan(vm内OpenWrt接口)改成vnet0(Host上由KVM自动创建的虚拟网卡)就解决问题了,对吧?试一把。
结果:又双叒叕可耻的失败了。
其一,流量经过。其二,出接口。靠谱如斯,还能出什么问题呢?
“观察iptables包计数器,值为0,没有一个包经过。”
明明有流量,tcpdump抓包证实的,去哪儿了呢?
仔细想想就明白了。Linux bridge是个二层设备,iptables是三层工具,二层流量虽然经过Host机网卡(end0.12)没错,但目标是直奔OpenWrt vm的eth0(的mac address)而去,在Host机上不管mangle还是nat表都不见踪影。既然无法标记流量,也就无法做到限速,没毛病吧。
事已至此,离最终目标的达成只差一步。
“难道要改成Host机拨PPPoE,而OpenWrt eth1再多走一层NAT?”
这也太不“优雅”了,跟关闭软件流量分载可以说不分伯仲,不是俺的菜。
“那如何在二层网桥上标记流量呢?”
答案是,ebtables,粉丝量与主要工作在三层的iptables完全没有可比性,但它生来就是解决二层问题的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#!/usr/bin/bash TC=/usr/sbin/tc EBT=/usr/sbin/ebtables TGT_ETH="vnet0" SRC_MAC="12:34:56:78:9a:bc" RATE="4mbit" CEIL="8mbit" echo "" echo "== Curr time: `date +"%Y-%m-%d %H:%M:%S"` " echo "" echo "== Get ready to set up qdisc. Let's show the current settings: " $TC -s -d class show dev $TGT_ETH $EBT -L echo "" echo "== Here we go set up... " $TC qdisc del dev $TGT_ETH root $TC qdisc add dev $TGT_ETH root handle 10: htb default 30 $TC class add dev $TGT_ETH parent 10: classid 10:1 htb rate 1000mbit $TC class add dev $TGT_ETH parent 10:1 classid 10:10 htb rate $RATE ceil $CEIL $TC class add dev $TGT_ETH parent 10:1 classid 10:80 htb rate 1000mbit $TC filter add dev $TGT_ETH parent 10:0 protocol ip prio 10 handle 10 fw flowid 10:10 $EBT -t filter -F $EBT -t filter -X $EBT -t filter -A FORWARD -s $SRC_MAC -j mark --mark-set 10 --mark-target ACCEPT echo "" echo "== The qdisc should be done there. Let's double check: " $TC -s -d class show dev $TGT_ETH $EBT -L |
实测?不打折扣,问题解决。
其中SRC_MAC为电视的mac地址,感觉ebtables实现比iptables更便利,可以不需要给电视设置固定ip或者dhcp分配静态ip,堪称巴适。
回头再看这个问题,路由的虚拟化提供了什么?
在下愚见,在性能损失可接受的前提下,提供在面对特殊技术需求时的「极致灵活性」。
文章的脚注信息由WordPress的wp-posturl插件自动生成
确实Flow Offloading会让qos失效,看怎么权衡了
没错,而此处就是写了一种因虚拟化实现让二者共存的玩法,尽管在arm上做虚拟化的性能损失比x86要更加明显。