前言

我这里使用的是树莓派烧录的openwrt,本地编译我试了很多次,都失败了,我建议小白就去大神搭建的云编译平台openwrt。烧录固件的软件我用的树莓派官方的烧录器。

云编译

openwrt。填上我们需要的一些软件包,把后面的ipv6打上勾,之后就是下载->烧录->启动

1luci-app-ttyd mod-rkp-ipid iptables-mod-filter iptables-mod-ipopt iptables-mod-u32 iptables-nft kmod-ipt-ipopt ipset iptables-mod-conntrack-extra

image-20250328183258795

联网

开机之后,用电脑连接上wifi名字叫Kwrt_5G,然后用网线让树莓派连接上校园网,增加一个wan口。wan口设备选择的是br-lan口,用的dhcp协议

image-20250328184509650

之后会跳出认证界面,这个认证界面的mac地址是树莓派的地址,登录你的账号密码,就可以上网了

设置ntp服务器

image-20250328184146069

1ntp.aliyun.com
2time1.cloud.tencent.com
3time.ustc.edu.cn
4cn.pool.ntp.org

添加开机启动脚本

image-20250328184305879

 1# 启动 UA3F
 2uci set ua3f.enabled.enabled=1
 3uci commit ua3f
 4service ua3f enable
 5service ua3f start
 6
 7#防火墙:
 8iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 53
 9iptables -t nat -A PREROUTING -p tcp --dport 53 -j REDIRECT --to-ports 53
10
11# 防 IPID 检测
12iptables -t mangle -N IPID_MOD
13iptables -t mangle -A FORWARD -j IPID_MOD
14iptables -t mangle -A OUTPUT -j IPID_MOD
15iptables -t mangle -A IPID_MOD -d 0.0.0.0/8 -j RETURN
16iptables -t mangle -A IPID_MOD -d 127.0.0.0/8 -j RETURN
17# 由于本校局域网是 A 类网,所以我将这一条注释掉了,具体要不要注释结合你所在的校园网内网类型
18# iptables -t mangle -A IPID_MOD -d 10.0.0.0/8 -j RETURN
19iptables -t mangle -A IPID_MOD -d 172.16.0.0/12 -j RETURN
20iptables -t mangle -A IPID_MOD -d 192.168.0.0/16 -j RETURN
21iptables -t mangle -A IPID_MOD -d 255.0.0.0/8 -j RETURN
22iptables -t mangle -A IPID_MOD -j MARK --set-xmark 0x10/0x10
23
24# 防时钟偏移检测
25iptables -t nat -N ntp_force_local
26iptables -t nat -I PREROUTING -p udp --dport 123 -j ntp_force_local
27iptables -t nat -A ntp_force_local -d 0.0.0.0/8 -j RETURN
28iptables -t nat -A ntp_force_local -d 127.0.0.0/8 -j RETURN
29iptables -t nat -A ntp_force_local -d 192.168.0.0/16 -j RETURN
30iptables -t nat -A ntp_force_local -s 192.168.0.0/16 -j DNAT --to-destination 192.168.1.1
31
32# 通过 iptables 修改 TTL 值
33iptables -t mangle -A POSTROUTING -j TTL --ttl-set 64
34
35# iptables 拒绝 AC 进行 Flash 检测
36# iptables -I FORWARD -p tcp --sport 80 --tcp-flags ACK ACK -m string --algobm --string " src=\"http://1.1.1." -j DROP
37iptables -A FORWARD -p tcp --sport 80 --tcp-flags ACK ACK -m string --algo bm --string "src=\"http://1.1.1." -j DROP

安装ua3f和ShellClash

找到服务->终端

1#从URL安装ShellClash(以下链接三选一)
2#GitHub源(可能需要代理)
3export url='https://raw.githubusercontent.com/juewuy/ShellCrash/master' && sh -c "$(curl -kfsSl $url/install.sh)" && source /etc/profile &> /dev/null
4#jsDelivrCDN源
5export url='https://fastly.jsdelivr.net/gh/juewuy/ShellCrash@master' && sh -c "$(curl -kfsSl $url/install.sh)" && source /etc/profile &> /dev/null
6#私人源
7export url='https://gh.jwsc.eu.org/master' && sh -c "$(curl -kfsSl $url/install.sh)" && source /etc/profile &> /dev/null

image-20250407180821163

image-20250407181057614

image-20250407181136304

image-20250407181216144

image-20250407181238614

image-20250407181250621

image-20250407181313829

1#用于UA3F的Clash配置(无外部代理)
2https://cdn.jsdelivr.net/gh/SunBK201/UA3F@master/clash/ua3f-cn.yaml

image-20250407181402017

image-20250407192730832

image-20250407192805640 image-20250407192832533 image-20250407192912823 image-20250407192933768 image-20250407192953799 image-20250407193025170

启动crash会出现两行报错,下面步骤是问了deepseek解决的

  1. 切换iptables后端的终极方法

如果系统同时安装了iptables-legacyiptables-nft,直接通过符号链接强制指定:

 1# 备份原有命令
 2mv /usr/sbin/iptables /usr/sbin/iptables.bak
 3mv /usr/sbin/ip6tables /usr/sbin/ip6tables.bak
 4
 5# 链接到legacy版本
 6ln -s /usr/sbin/iptables-legacy /usr/sbin/iptables
 7ln -s /usr/sbin/ip6tables-legacy /usr/sbin/ip6tables
 8
 9# 验证版本
10iptables --version
  1. 检查系统日志定位具体错误
1# 查看内核日志(重点关注nf_nat相关错误)
2dmesg | grep -i "nat\|conntrack\|iptables"
3
4# 查看ShellClash日志
5logread | grep "shellclash"
  1. 测试手动添加PREROUTING规则
1# 使用绝对路径强制操作
2/usr/sbin/iptables-legacy -t nat -N PREROUTING
3/usr/sbin/iptables-legacy -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 7890
4
5# 检查是否生效
6iptables-legacy -t nat -L PREROUTING

然后再启动crash -s start或者crash然后输入1

安装UA3F

1#从URL安装UA3F
2opkg update
3opkg install curl libcurl luci-compat
4export url='https://blog.sunbk201.site/cdn' && sh -c "$(curl -kfsSl $url/install.sh)"
5service ua3f reload

启动UA3F

1#设置UA3F自动启动
2# 启动 UA3F
3uci set ua3f.enabled.enabled=1
4uci commit ua3f
5service ua3f start

1.配置静态IP(二选一)

如果你觉得方法2太麻烦,可以用这个方法

  • 首先重启一下openwrt
  • 手机或电脑连接openwrt的wifi,配置静态ip
  • 比如:我的lan接口是192.168.1.1
    • IP地址192.168.1.*(*表示2-254)
    • 网关就是lanIP地址
    • 子网掩码一般都是255.255.255.0
    • dns配一个8.8.8.8就行

这样你就能访问openwrt后台了,然后直接去校园网认证界面,看到导航栏的mac地址是openwrt的地址,然后登录校园网,这样就是openwrt去获取到的校园网ip,在这个openwrt网段下的设备都可以上网。

2.安装python3配置认证脚本(二选一)

打开终端

1opkg update
2opkg install python3
3opkg install python3-yaml

在root目录下创建两个py文件

1touch ruijie.py
2touch config.py

下面是ruijie.py代码

  1#!/usr/bin/env python3
  2# -*- coding: utf-8 -*-
  3
  4import os
  5import sys
  6import fcntl  # 文件锁
  7import subprocess
  8from datetime import datetime
  9from json import loads as json_loads
 10from os.path import dirname, join
 11from urllib import parse, request
 12from urllib.request import urlopen
 13
 14from config import read_cfg
 15
 16# 配置基础路径
 17basedir = dirname(__file__) if not getattr(sys, 'frozen', False) else sys._MEIPASS
 18
 19cfg = read_cfg()
 20
 21headers = {
 22    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36',
 23    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
 24    'Cookie': parse.quote(cfg['cookie']),
 25    'Host': cfg['url']['server'].replace('http://', '').replace('https://', ''),
 26    'Origin': cfg['url']['server'].replace('http://', '').replace('https://', ''),
 27}
 28
 29headers.update(cfg['headers'])
 30
 31login_data = cfg['login_data']
 32logout_data = cfg['logout_data']
 33
 34def notify(title: str, msg: str):
 35    """命令行输出通知"""
 36    print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {title}: {msg}")
 37
 38def test_internet(host: str = 'http://connect.rom.miui.com/generate_204', timeout: int = 1) -> bool:
 39    """检测网络连通性(适配OpenWrt)"""
 40    try:
 41        # 使用ping检测基础网络
 42        if subprocess.run(["ping", "-c", "1", "8.8.8.8"], timeout=2).returncode == 0:
 43            return True
 44        # 补充HTTP检测
 45        resp = urlopen(host, timeout=timeout)
 46        return resp.status == 204 if host.endswith('generate_204') else 200 <= resp.status <= 208
 47    except Exception:
 48        return False
 49
 50def connect():
 51    """执行登录操作"""
 52    req = request.Request(
 53        cfg['url']['server'] + cfg['url']['login'],
 54        data=parse.urlencode(login_data).encode() if login_data else None,
 55        headers=headers,
 56        method='POST'
 57    )
 58    try:
 59        with request.urlopen(req, timeout=10) as res:
 60            status = json_loads(res.read().decode())
 61            if status['result'] == 'success':
 62                notify('联网成功', status.get('message', '网络已连接!'))
 63            else:
 64                notify('联网失败', status.get('message', '未知错误'))
 65    except Exception as e:
 66        notify('错误', f'连接失败: {str(e)}')
 67        sys.exit(1)
 68
 69def disconnect():
 70    """执行注销操作"""
 71    req = request.Request(
 72        cfg['url']['server'] + cfg['url']['logout'],
 73        data=parse.urlencode(logout_data).encode(),
 74        headers=headers,
 75        method='POST'
 76    )
 77    try:
 78        with request.urlopen(req, timeout=10) as res:
 79            status = json_loads(res.read().decode())
 80            if status['result'] == 'success':
 81                notify('断网成功', '已断开网络!')
 82            else:
 83                notify('断网失败', status.get('message', '未知错误'))
 84    except Exception as e:
 85        notify('错误', f'断网失败: {str(e)}')
 86        sys.exit(1)
 87
 88def main():
 89    # 单实例检测(文件锁)
 90    lockfile = open('/tmp/ruijie.lock', 'w')
 91    try:
 92        fcntl.flock(lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
 93    except BlockingIOError:
 94        notify('警告', '程序已在运行,禁止多开!')
 95        sys.exit(0)
 96
 97    # 网络检测逻辑
 98    if cfg['funtion']['check_school_network']:
 99        if not subprocess.run(["ping", "-c", "1", "172.31.0.3"], timeout=2).returncode == 0:
100            notify('环境检测', '未连接到校园网,程序退出')
101            sys.exit(0)
102
103    if test_internet():
104        if cfg['funtion']['disconnect_network']:
105            disconnect()
106        else:
107            notify('状态', '网络已连接,无需操作')
108    else:
109        connect()
110
111if __name__ == '__main__':
112    main()

找到"172.31.0.3"改成你校园网的ip

下面是config.py的代码

 1#!/usr/bin/env python3
 2# -*- coding: utf-8 -*-
 3
 4import sys
 5import os
 6from os.path import dirname, join
 7import yaml
 8
 9current_config_version = 3
10config_path = join(dirname(sys.argv[0]), 'config.yml')
11
12def str_value(target: dict):
13    """递归转换值为字符串(保持原有逻辑)"""
14    for k, v in target.items():
15        if isinstance(v, dict):
16            str_value(v)
17        elif isinstance(v, (int, bool)):  # 处理整型和布尔值
18            target[k] = str(v).lower()
19        elif v is None:
20            target[k] = ''
21    return target
22
23def write_config():
24    """生成默认配置文件(移除GUI弹窗)"""
25    config_template = '''\
26# 本配置文件内容需要根据学校服务器设置动态调整
27main:
28  version: 3 # 配置文件版本号,请勿更改
29
30funtion:
31  check_school_network: true
32  disconnect_network: true
33
34url:
35  server: http://172.31.0.3
36  login: /eportal/InterFace.do?method=login
37  logout: /eportal/InterFace.do?method=logout
38
39# ...(其他配置项保持原样,此处省略)...
40'''
41    try:
42        with open(config_path, 'w', encoding='utf-8') as fp:
43            fp.write(config_template)
44        print(f"[INFO] 已生成新配置文件: {config_path}")
45    except Exception as e:
46        print(f"[ERROR] 配置文件创建失败: {str(e)}")
47        sys.exit(1)
48
49def read_cfg() -> dict:
50    """读取配置文件(命令行交互版)"""
51    # 检查配置文件是否存在
52    if not os.path.exists(config_path):
53        write_config()
54        print("[WARN] 配置文件不存在,已生成模板,请修改后重试!")
55        sys.exit(1)
56
57    # 读取配置内容
58    try:
59        with open(config_path, 'r', encoding='utf-8') as fp:
60            cfg = yaml.safe_load(fp)
61    except Exception as e:
62        print(f"[ERROR] 配置文件读取失败: {str(e)}")
63        sys.exit(1)
64
65    # 版本校验
66    config_version = cfg['main'].get('version', 0)
67    if config_version != current_config_version:
68        print(f"[ERROR] 配置文件版本不兼容(当前版本要求:{current_config_version})")
69        sys.exit(1)
70
71    # 数据预处理
72    for key in ['url', 'login_data', 'logout_data']:
73        if key in cfg and cfg[key] is not None:
74            cfg[key] = str_value(cfg[key])
75
76    # 基础校验
77    if cfg['login_data'].get('userId') == '00000000000':
78        print("[ERROR] 请修改配置文件中的userId字段")
79        sys.exit(1)
80
81    # URL格式修正
82    if cfg['url']['server'].endswith('/'):
83        cfg['url']['server'] = cfg['url']['server'][:-1]
84
85    return cfg

然后用命令python ruijie.py运行,然后会多出来一个config.yml文件

配置config.yaml文件

由于我们wifi连接的树莓派,mac地址是树莓派的地址,如果地址栏mac地址是你的电脑,那么你需要修改一下mac地址,然后再进行此操作。断开校园网,用电脑再次认证一遍,认证之前需要打开F12

image-20250328192042282

image-20250328192509309

image-20250328192533899

我打####的地方都需要填,根据上面你获取的信息填

 1# 本配置文件内容需要根据学校服务器设置动态调整
 2main:
 3  version: 3 # 配置文件版本号,请勿更改
 4
 5funtion:
 6  check_school_network: true # 是否检查校园网环境
 7  disconnect_network: true # 是否开启断网功能
 8
 9url:
10         #####改成你学校的ip地址
11  server: http://172.31.0.3 # 校园网登录服务器的地址,用于判断当前网络环境是否为校园网环境
12  login: /eportal/InterFace.do?method=login # 校园网登录地址,无需服务器地址
13  logout: /eportal/InterFace.do?method=logout # 校园网断线地址,无需服务器地址
14
15# 请根据抓包结果调整条目与内容
16cookie: '##########'
17
18# 请根据抓包结果调整条目与内容(?key1=value1&key2=value2)
19# 请不要在密码栏填入明文密码
20login_data:
21  userId: '##########'
22  password: ##########
23
24  service: '#######'
25  queryString: #########
26  operatorPwd:
27  operatorUserId:
28  validcode:
29  passwordEncrypt: true
30
31# 一般来说不填参数也能用,但请不要删除(?key1=value1&key2=value2)
32logout_data: {}
33
34headers:
35  Referer: ############

把改好的信息填入你的config.yaml
退出的校园网,终端运行python ruijie.py看看是否能连接成功
http://ua.233996.xyz/打开这个网站,看看是否被修改成FFF

image-20250328193913829

如果没有成功,可以看看是不是ShelClash没有运行,如果运行了,并且网络也能ping通,那么多刷新几遍试试,或者重启一下试试

定时断网重连

点开管控>任务设置>定时执行任务自定义脚本写下面代码

1if ! ping -c 1 8.8.8.8 >/dev/null 2>&1; then  
2       crash -s stop
3        sleep 10
4        /usr/bin/python3 /root/ruijie.py &  
5       sleep 10
6       crash -s start
7fi 

添加一条任务,记得打上对勾,最后面框框是1分钟执行一次,你可以按需更改,最后记得保存

image-20250406110204329

我遇到过的问题

  1. 重启的话网络可能会没有自动连接上,那么就手动连接一下,再启动ShellClash。目前还没有找到100%可以自动启动的方法。

  2. 如果你的电脑连接上wifi并且可以上网,其他设备连接上会跳认证界面,你可以找到网络->接口把wan口删掉然后创建一个静态的wan口保存一下,然后再删了wan修改回去。然后电脑断掉wifi再连接,基本上就可以了。

  3. 有陌生设备连接时会断网,在终端再运行一遍python ruijie.py认证一下,目前没有找到更好的办法。

  4. 把2做了一遍后手机连接还是跳认证界面,将wifi关掉再打开,或者切换一下网络,然后再重新连接就可以了。

  5. 终端不显示,etc/init.d/ttydimage-20250407201653519

  6. 如果你让openwrt用wifi连接的校园网,你再开热点的话,会遇到打不开的情况,输入下面的代码

    1rm /etc/config/wireless
    2wifi config
    3wifi down
    4wifi up
    

参考

  1. https://www.bilibili.com/video/BV1yr4meeENt/?spm_id_from=333.1391.0.0
  2. https://www.bilibili.com/video/BV1qM411w7W5/?spm_id_from=333.1391.0.0&vd_source=bde3073c7fac1db05c5ea47eed6aa6a6
  3. https://github.com/IDeLoveYou/SGU-Script认证脚本是在这个作者代码的基础上更改的

吐槽

这玩意折磨了我两个星期,太难受了,走了好多弯路🥲