宽带就是互联网的高速公路,带宽就是这公路的宽度,根据统计,要流式播放4K视频,至少需要12Mbps带宽,而校园网的限制带宽是14Mbps,所以播放4K视频拖动进度条时不流畅。于是合理利用校园网两台设备在线的规则,实现带宽叠加非常重要。
环境介绍
校园网目前限速14Mbps,同一个帐号允许两台设备同时使用。路由器为树莓派4,使用Ubuntu Server Armx64系统,使用有线网络连接到校园网,校园网使用锐捷认证,因此认证软件使用MentoHUST。
准备工作
单线多号
首先要实现单线多号。这一点不一定所有学校都支持,关键在于学校接入端设备是否限制了每个端口连接的设备数量。因为实现单线多号,相当于给网口接了个傻交换,交换机下又接了几台设备,每个设备分别认证。
使用macvlan创建虚拟网卡
首先启用macvlan(只要是新一点的Linux内核都有)
modprobe macvlan
lsmod | grep macvlan # 查看是否启用成功
然后在物理网卡上创建虚拟网卡(eth0macv0
名称自定)
ip link add link eth0 eth0macv0 type macvlan
ip link set eth0macv0 up
ip link set eth0macv0 promisc on
此时可以打开ifconfig
查看一下网卡是否up
多开mentohust
mentohust运行时会创建pid文件,只要该文件存在mentohust就会拒绝启动。我们手动删除即可(如果使用systemd管理mentohust的话,要注意修改,不要用pid文件是否存在来判断服务状态)
rm /var/run/mentohust.pid
接下来启动mentohust,也可修改配置文件启动(mentohust只会在启动瞬间读取配置)。注意修改网卡为eth0macv0
:
mentohust -u xxx -p xxx -i eth0macv0 ...
如果都认证成功,则说明学校设备支持单线多认证。
测试单线多号限速
如果学校设备是对一条线路上的所有设备限速,那也白搭。所以我们需要测试在一条线路上各个帐号是分别限速还是一起限速。这个我们等所有工作完成后再测试。
删除默认网关
注意:不需要删除默认网关,如果你需要删除默认网关才能实现负载均衡,那么可能是有某一步出错了。
删除默认网关:
ip route del default
ip route del default # 多执行几次
现在查看路由表,会发现已经没有了默认路由(default):
$ ip route
125.216.222.0/23 dev eth0 proto kernel scope link src 125.216.222.123
172.17.0.0/16 dev eth1 proto kernel scope link src 172.17.1.1
删除默认路由的原因是,默认路由可能会影响到后续iptables进行NAT转换时,直接使用默认网关路由作为MASQUERADE的地址,导致只有一条线路实际承载流量,导致均衡失效。
NAT
因为刚刚新建了新的出口,别忘了给新的出口加上NAT策略:
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
iptables -t nat -A POSTROUTING -o eth0macv0 -j MASQUERADE
接下来就可以开始编写具体的负载均衡策略了。
负载均衡策略
我们使用的是基于连接的负载均衡其优点在于通用性好,且易于实现。要实现负载均衡,核心思想是对所有的新建连接(即NEW的连接)进行均匀的标记,不同的标记导向不同的路由表,实现多线负载。
路由表
Linux内部可以有多个路由表(ip route
系列命令),选择哪个路由表是通过数据包规则(ip rule
系列命令)确定。现在我们已经有两个网络出口,一个是eth0
,一个是eth0macv0
。我们可以使用路由表100
表示要通过eth0
发出的路由,用路由表101
表示要通过eth0macv0
发出的路由。
# 路由表100 出口eth0
ip route add default via 125.216.222.1 dev eth0 table 100
# 路由表101 出口eth1
ip route add default via 125.216.222.1 dev eth0macv0 table 101
除此之外可能还需要将主路由表的其他条目复制到这两个表中,这里有一个简便方法(来源https://staff.ie.cuhk.edu.hk/~sfluk/wordpress/?p=3181):
ip route show table main | grep -Ev '^default' | while read ROUTE ; do
ip route add table 100 $ROUTE
ip route add table 101 $ROUTE
done
标记连接
核心思想即为,对每一个新建的连接,都加上标记,然后追踪这个连接,同时将数据包打上相应的标记。在连接追踪表中标记是利于识别将整个连接的数据包,对每个数据包打标记是为了在之后按照策略路由往不同出口:
# 标记 从远端发回的数据包
iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
# 标记 从本地新建连接的数据包 到 连接追踪表
iptables -t mangle -A PREROUTING -m conntrack --ctstate NEW -m statistic --mode nth --every 2 --packet 0 -j CONNMARK --set-mark 10
iptables -t mangle -A PREROUTING -m conntrack --ctstate NEW -m statistic --mode nth --every 2 --packet 1 -j CONNMARK --set-mark 11
# 标记 刚刚新建的连接 到 数据包
iptables -t mangle -A PREROUTING -m connmark --mark 10 -j MARK --set-mark 10
iptables -t mangle -A PREROUTING -m connmark --mark 11 -j MARK --set-mark 11
标记部分使用的是轮询方式,也可以使用随机方式:
# 标记 从本地新建连接的数据包 到 连接追踪表
iptables -t mangle -A PREROUTING -m conntrack --ctstate NEW -m statistic --mode random --probability 0.5 -j CONNMARK --set-mark 10
iptables -t mangle -A PREROUTING -m conntrack --ctstate NEW -j CONNMARK --set-mark 11
这两行命令表示0.5的概率命中上面的规则,在连接表中标记为10
,其余的标记为11
。
数据包规则
连接标记了之后,需要使用一定的方式将不同标记的数据包导向不同路由表,从不同出口发出。(如标记为10的数据包就发往100路由表):
ip rule add fwmark 10 table 100
ip rule add fwmark 11 table 101
关闭rp_filter
rp_filter是Linux防止攻击人伪装IP地址向本机发送数据的功能,在数据包从远端发回的时候,将数据包源地址和目标地址对掉再次查找路由表。如果数据包的源地址可达且为最短路径,则接受。但不知为何在当前配置下系统并不能通过检验,所以只能将此功能关闭。这个问题有待进一步探究。
sysctl -w net.ipv4.conf.all.rp_filter=0
sysctl -w net.ipv4.conf.eth0.rp_filter=0
sysctl -w net.ipv4.conf.eth0macv0.rp_filter=0
至此就结束了所有的配置,可以尝试测速看看带宽是否叠加吧。
如果需要叠加更多线路,相应地多添加几个macvlan接口、配置多几条规则即可。
总结
在传输层对连接会话进行负载均衡是目前最广泛使用的方法。这种方法有利有弊。优点是配置简单,无需外部服务器辅助、整合流量,但缺点是每个连接的速度还是被限制了的。在一些网站看视频的时候,若使用的是单连接,则播放视频仍然被限速。不过目前的大型视频网站都采用了分段加载的方式,每个会话加载一段视频,此时使用多线负载,可能能改善视频播放。
4线负载speedtest.net结果:
6线:
最终脚本
#!/bin/bash
modprobe macvlan
ip link add link eth0 eth0macv0 type macvlan
ip link set eth0macv0 up
ip link set eth0macv0 promisc on
sysctl -w net.ipv4.conf.all.rp_filter=0
sysctl -w net.ipv4.conf.eth0.rp_filter=0
sysctl -w net.ipv4.conf.eth0macv0.rp_filter=0
systemctl stop mentohust
systemctl start mentohust-me@eth0 # 见附录,手动认证的话记得删除pid文件
systemctl start mentohust-another@eth0macv0
iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -m conntrack --ctstate NEW -m statistic --mode nth --every 4 --packet 0 -j CONNMARK --set-mark 10
iptables -t mangle -A PREROUTING -m conntrack --ctstate NEW -m statistic --mode nth --every 4 --packet 1 -j CONNMARK --set-mark 11
iptables -t mangle -A PREROUTING -m connmark --mark 10 -j MARK --set-mark 10
iptables -t mangle -A PREROUTING -m connmark --mark 11 -j MARK --set-mark 11
iptables -t nat -A POSTROUTING -o eth0macv0 -j MASQUERADE
ip route add default via 125.216.222.1 dev eth0 table 100
ip route add 172.17.0.0/16 dev eth1 table 100
ip route add default via 125.216.222.1 dev eth0macv0 table 101
ip route add 172.17.0.0/16 dev eth1 table 101
ip rule add fwmark 10 table 100
ip rule add fwmark 11 table 101
IPv6
IPv6负载均衡原理类似,但要注意在某些环境下,可能需要将各个macvlan网卡的accept_ra
设为2
。
脚本如下:
sysctl -w net.ipv6.conf.eth0macv0.accept_ra=2
ip6tables -t mangle -A PREROUTING -j CONNMARK --restore-mark
ip6tables -t mangle -A PREROUTING -m conntrack --ctstate NEW -m statistic --mode nth --every 4 --packet 0 -j CONNMARK --set-mark 10
ip6tables -t mangle -A PREROUTING -m conntrack --ctstate NEW -m statistic --mode nth --every 4 --packet 1 -j CONNMARK --set-mark 11
ip6tables -t mangle -A PREROUTING -m conntrack --ctstate NEW -m statistic --mode nth --every 4 --packet 2 -j CONNMARK --set-mark 12
ip6tables -t mangle -A PREROUTING -m conntrack --ctstate NEW -m statistic --mode nth --every 4 --packet 3 -j CONNMARK --set-mark 13
ip6tables -t mangle -A PREROUTING -m connmark --mark 10 -j MARK --set-mark 10
ip6tables -t mangle -A PREROUTING -m connmark --mark 11 -j MARK --set-mark 11
ip6tables -t mangle -A PREROUTING -m connmark --mark 12 -j MARK --set-mark 12
ip6tables -t mangle -A PREROUTING -m connmark --mark 13 -j MARK --set-mark 13
ip6tables -t nat -A POSTROUTING -o eth0macv0 -j MASQUERADE
ip -6 route show table main | grep -Ev '^default' | while read ROUTE ; do
ip -6 route add table 100 $ROUTE
ip -6 route add table 101 $ROUTE
done
ip -6 route add default via fe80::1614:4bff:fe80:3b81 dev eth0 table 100
ip -6 route add default via fe80::1614:4bff:fe80:3b81 dev eth0macv0 table 101
ip -6 rule add fwmark 10 table 100
ip -6 rule add fwmark 11 table 101
如果看到多次刷新页面时显示的地址都不一样,且速度有所提升,则说明多线负载成功。
附录
Mentohust服务编写
推荐使用systemd管理mentohust认证服务:
编写并保存到/etc/systemd/system/mentohust-username1@.service
[Unit]
Description=MentoHUST Ruijie Auth User xxx
Requires=network-online.target
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
ExecStartPost=rm /var/run/mentohust.pid
ExecStart=/usr/bin/mentohust -u username1 -p "xxxxx" -n %I -a 1 -d 3
Restart=always
RestartSec=5s
StartLimitInterval=5
StartLimitBurst=10
[Install]
WantedBy=multi-user.target
将各个账户和密码保存到不同的服务文件中,之后就能用如systemctl start mentohust-username1@eth0
在eth0上用username1认证。
写一套服务程序来管理负载均衡
事实上,负载均衡还有很多要做。首先我们可以注意到,脚本中大量语句是重复的,且要修改每个帐号与网口的对应关系,就需要更改大量语句,而这一切都应该可以有程序自动完成,节省人力成本。此外,要有链路监控功能。比如某一时刻某位同学离开了宿舍,在别处登陆了网络,那么路由器这边的设备就会被挤下线。所以需要一套监控程序监控当前连接,当该线路连接中断时,立即重新分配负载线路。还有,要完善ipv6的负载均衡。
项目仓库:https://git.mewwoof.cn/JourneyBean/iptables-load-balance