校园网实现带宽叠加

宽带就是互联网的高速公路,带宽就是这公路的宽度,根据统计,要流式播放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接口、配置多几条规则即可。

总结

在传输层对连接会话进行负载均衡是目前最广泛使用的方法。这种方法有利有弊。优点是配置简单,无需外部服务器辅助、整合流量,但缺点是每个连接的速度还是被限制了的。在一些网站看视频的时候,若使用的是单连接,则播放视频仍然被限速。不过目前的大型视频网站都采用了分段加载的方式,每个会话加载一段视频,此时使用多线负载,可能能改善视频播放。

B站加载视频的时间线(负载均衡前)
负载均衡后(4线路负载均衡)
负载均衡后视频统计信息
几条出口线路流量统计

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
IPv6测速,由于一般使用4个连接测速,所以跑不满带宽

如果看到多次刷新页面时显示的地址都不一样,且速度有所提升,则说明多线负载成功。

附录

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

发表评论