OpenWrt 校园网单线多拨与负载均衡自动登录脚本
本指南介绍如何在 OpenWrt (WR30U) 下通过 macvlan 批量创建虚拟网卡,实现单线多拨叠加带宽,并配合自动化脚本完成多账号认证。
一、 刷入 OpenWrt 固件
1. 开启 Telnet 与 SSH
由于 WR30U 是运营商定制版,通常需要先通过小米官方修复工具或特定漏洞开启 Telnet。
获取权限: 使用 Bilibili 或 GitHub 上常见的 “WR30U 开启 Telnet 工具”。
登录: 开启后通过终端执行 telnet 192.168.31.1,用户名为 root,密码根据 SN 计算或为空。
固化 SSH: 进入后修改 nvram 参数以永久开启 SSH 权限,并修改 root 密码。
2. 刷入 uboot (不死固件)
刷入 uboot 是为了防止以后折腾固件导致“变砖”,建议选择 Hanwckf 的 MT7981 版 uboot。
传输文件: 使用 SCP 将 uboot.bin 上传至路由器的 /tmp 目录。
写入: 执行以下命令(注意:分区名称务必核对为 FIP):
# 确认分区
cat /proc/mtd
# 写入 uboot
mtd write /tmp/mt7981_wr30u-fip-fixed-parts.bin FIP
3. 进入 uboot 刷入系统
物理操作: 断开电源,按住 Reset 键不放,插电,约 5-10 秒后灯闪烁。
访问界面: 电脑网线连接 LAN 口,手动设置 IP 为 192.168.1.2,浏览器访问 192.168.1.1。
上传固件: 在 uboot 界面选择你的 OpenWrt .bin 固件(选择内置了 mwan3 和 macvlan 插件的编译版本),点击 Flash。
重启: 进度条走完后,路由器会自动重启进入 OpenWrt 界面(通常为 192.168.1.1)。
二、 网络基础配置
为了确保脚本能准确识别接口,虚拟网卡与接口名称必须严格遵循 veth{ID} 与 vwan{ID} 的命名格式。
1. 批量创建虚拟网卡 (Device)
直接编辑 /etc/config/network,为每个拨号实例添加以下配置(MAC 地址需随机且唯一):
config device 'veth0'
option name 'veth0'
option ifname 'wan' # 物理 WAN 口名称
option type 'macvlan'
option macaddr '76:6F:7C:36:E0:aa'
config device 'veth1'
option name 'veth1'
option ifname 'wan'
option type 'macvlan'
option macaddr '76:6F:7C:36:E0:bb'
2. 批量创建逻辑接口 (Interface)
在 /etc/config/network 中继续添加接口定义。注意:每个接口的 metric 必须唯一,否则负载均衡无法生效。
config interface 'vwan0'
option proto 'dhcp'
option ifname 'veth0'
option metric '10'
config interface 'vwan1'
option proto 'dhcp'
option ifname 'veth1'
option metric '20'
修改完成后,执行 /etc/init.d/network restart 重启网络服务。
三、 自动化认证脚本部署
脚本将根据配置文件中的 veth_id 自动寻找对应的虚拟网卡 (veth) 和逻辑接口 (vwan)。
1. 创建配置文件 (/usr/bin/xxxx_login/xxxx_login.conf)
确保 veth_id 与上述网络配置中的后缀一致。
{
"login_url": "http://192.168.200.2:801/eportal/",
"accounts": [
{
"veth_id": "0",
"is_pc": "0",
"username": "统一ID",
"company": "cmcc",
"password": "your_password"
},
{
"veth_id": "1",
"is_pc": "1",
"username": "统一ID",
"company": "cmcc",
"password": "your_password"
}
]
}
2. 认证脚本逻辑说明
脚本核心逻辑通过 --interface 参数强制 curl 走指定的虚拟网卡,从而绕过 OpenWrt 默认路由,实现对特定物理线路的认证。
脚本存储路径: /usr/bin/xxxx_login/xxxx_login.sh (需 chmod +x)
四、 设置定时任务 (Cron)
根据您的要求,将自动认证任务设置为每天每 6 小时执行一次(0点、6点、12点、18点),以防止学校系统强制下线。
在终端输入
crontab -e或在 Web 界面进入 系统 -> 计划任务。添加以下配置:
# 每天 0, 6, 12, 18 点整执行认证脚本
0 0,6,12,18 * * * /usr/bin/xxxx_login/xxxx_login.sh
五、 负载均衡 (MWAN3) 关键点
配置好接口后,请在 MWAN3 插件中:
添加接口: 将
vwan0,vwan1等加入 MWAN3 接口列表。追踪地址: 设置可靠的 ping 测试地址(如
223.5.5.5),用于判断接口是否在线。分配权重: 在
Member设置中,如果带宽相同,将Weight均设为1。策略: 确保默认策略使用了包含所有
vwan成员的负载均衡方案。
详细可以自行搜索教程。
附录
xxxx_login.sh
#!/bin/sh
# 依赖检查
command -v jq >/dev/null || { echo "请先安装jq: opkg update && opkg install jq"; exit 1; }
command -v curl >/dev/null || { echo "请先安装curl: opkg update && opkg install curl"; exit 1; }
CONFIG_FILE="/usr/bin/xxxx_login/xxxx_login.conf"
LOG_FILE="/usr/bin/xxxx_login/xxxx_login.log"
MAX_RETRY=3 # 最大重试次数
# 日志记录函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
logger -t "CampusAuth" "$1"
}
# 获取接口IP(使用设备名)
get_interface_ip() {
local device=$1
ip -o -4 addr show dev "$device" | awk '{print $4}' | cut -d'/' -f1
}
# 主认证函数(核心逻辑)
do_auth() {
local account=$1
local base_url=$2
# 自动生成设备名和接口名(veth1 <-> vwan1)
local veth_id=$(echo "$account" | jq -r '.veth_id')
local device="veth${veth_id}"
local interface="vwan${veth_id}"
# 初始IP获取
local ipaddr=$(get_interface_ip "$device")
[ -z "$ipaddr" ] && {
log "错误:接口 $interface 无初始IP地址"
return 1
}
# 构造基础请求参数(不含IP)
local user_account=",$(echo "$account" | jq -r '.is_pc'),$(echo "$account" | jq -r '.username')@$(echo "$account" | jq -r '.company')"
local base_params="
c=Portal&
a=login&
callback=dr1003&
login_method=1&
wlan_user_ipv6=&
wlan_user_mac=&
wlan_ac_ip=&
wlan_ac_name=&
jsVersion=3.3.3&
v=10048&
user_account=$user_account&
user_password=$(echo "$account" | jq -r '.password')&
wlan_user_ip=
"
base_params=$(echo "$base_params" | tr -d '\n' | tr -d ' ')
# 重试逻辑
local retry_count=0
while [ $retry_count -lt $MAX_RETRY ]; do
# 每次重试都重新获取当前IP
ipaddr=$(get_interface_ip "$device")
[ -z "$ipaddr" ] && {
log "错误:接口 $interface 无法获取IP"
return 1
}
# 动态更新URL中的IP参数
local auth_url="$base_url?${base_params}${ipaddr}"
log "尝试认证接口 $interface (IP: $ipaddr, 重试: $retry_count)"
# 发送请求
response=$(curl -sG --connect-timeout 10 --interface "$device" "$auth_url")
log "认证url: $auth_url"
# 空响应处理
if [ -z "$response" ]; then
retry_count=$((retry_count + 1))
log "第 $retry_count 次空响应,等待重新连接..."
sleep 3
continue
fi
# 成功响应判断
if echo "$response" | grep -q -E 'dr1003\(\{"result":"0".*|\{"result":"1".*msg":"\\u8ba4\\u8bc1\\u6210\\u529f'; then
log "$interface 认证成功 - 响应: $response"
return 0
else
log "$interface 认证失败 - 响应: $response"
retry_count=$((retry_count + 1))
sleep 3
fi
done
log "错误:接口 $interface 超过最大重试次数 ($MAX_RETRY)"
return 1
}
# 主流程
main() {
log "=== 开始校园网认证任务 ==="
[ ! -f "$CONFIG_FILE" ] && {
log "错误:配置文件 $CONFIG_FILE 不存在"
exit 1
}
base_url=$(jq -r '.login_url' "$CONFIG_FILE")
[ -z "$base_url" ] && {
log "错误:未配置login_url"
exit 1
}
jq -c '.accounts[]' "$CONFIG_FILE" | while read -r account; do
do_auth "$account" "$base_url"
done
log "=== 认证任务完成 ==="
log "==================="
}
main