├── clash ├── config.yaml ├── scripts │ ├── clash.cron │ ├── clash.inotify │ ├── updateSub.sh │ ├── clash.service │ ├── clash.tool │ └── clash.iptables ├── clash.config ├── clash.internal.config └── template ├── cacert-etag.txt ├── META-INF └── com │ └── google │ └── android │ ├── updater-script │ └── update-binary ├── module.prop ├── action.sh ├── .gitignore ├── uninstall.sh ├── service.sh ├── files.config ├── .github └── workflows │ ├── mihomo-alpha.yml │ ├── main.yml │ └── dev.yml ├── CHANGELOG.md ├── download_providers.py ├── README.md ├── customize.sh └── pack.sh /clash/config.yaml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cacert-etag.txt: -------------------------------------------------------------------------------- 1 | "38f2f-62eefa8c2e7b3" 2 | -------------------------------------------------------------------------------- /META-INF/com/google/android/updater-script: -------------------------------------------------------------------------------- 1 | #MAGISK -------------------------------------------------------------------------------- /module.prop: -------------------------------------------------------------------------------- 1 | id=Mihomo_For_Magisk 2 | name=Mihomo For Magisk 3 | version=v1.4.2 4 | versionCode=20250306 5 | author=kagura 6 | description=Mihomo for Magisk/KSU/APatch 模块, mihomo版本: 7 | -------------------------------------------------------------------------------- /action.sh: -------------------------------------------------------------------------------- 1 | config="/data/clash/run/config.yaml" 2 | controller=$(grep "external-controller:" ${config} | awk '{print $2}' | tr -d ' ') 3 | 4 | am start -a android.intent.action.VIEW -d "http://${controller}/ui" 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | release/* 2 | binary/* 3 | clash/Country.mmdb 4 | clash/GeoIP.dat 5 | clash/GeoSite.dat 6 | clash/config.yaml 7 | clash-dashboard/* 8 | clash/proxy_providers 9 | clash/rule_providers 10 | version 11 | *.gz -------------------------------------------------------------------------------- /clash/scripts/clash.cron: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | scripts=`realpath $0` 3 | scripts_dir=`dirname ${scripts}` 4 | . /data/clash/clash.config 5 | 6 | step=5 #间隔的秒数,不能大于60 7 | i=0 8 | while ((i < 60)) ; do 9 | i=$i+step 10 | ${scripts_dir}/clash.tool -m 11 | sleep $step 12 | done 13 | exit 0 -------------------------------------------------------------------------------- /uninstall.sh: -------------------------------------------------------------------------------- 1 | Clash_data_dir="/data/clash" 2 | Clash_old="/data/clash.old" 3 | 4 | rm_data() { 5 | if [ -z "${Clash_data_dir}" ]; then 6 | return 7 | fi 8 | rm -rf ${Clash_data_dir} 9 | 10 | if [ -z "${Clash_old}" ]; then 11 | return 12 | fi 13 | rm -rf ${Clash_old} 14 | } 15 | 16 | rm_data -------------------------------------------------------------------------------- /clash/clash.config: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | 3 | . /data/clash/clash.internal.config 4 | 5 | auto_updateSubcript="true" 6 | update_subcriptInterval="0 5 * * *" 7 | auto_updateGeoIP="false" 8 | auto_updateGeoSite="false" 9 | update_geoXInterval="0 5 * * 7" # 每周日的半夜5点更新GeoX 10 | 11 | # 下面内容理应与 打包时 files.config 一样 12 | Subcript_url="" 13 | GeoIP_dat_url="https://github.com/Loyalsoldier/geoip/releases/latest/download/cn.dat" 14 | Country_mmdb_url="https://github.com/Loyalsoldier/geoip/releases/latest/download/Country-only-cn-private.mmdb" 15 | GeoSite_url="https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat" -------------------------------------------------------------------------------- /clash/scripts/clash.inotify: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | 3 | scripts=`realpath $0` 4 | scripts_dir=`dirname ${scripts}` 5 | service_path="${scripts_dir}/clash.service" 6 | tproxy_path="${scripts_dir}/clash.iptables" 7 | signal_file="${scripts_dir}/../run/cmdRunning" 8 | 9 | events=$1 10 | monitor_dir=$2 11 | monitor_file=$3 12 | 13 | service_control() { 14 | if [ "${monitor_file}" = "disable" ] ; then 15 | touch ${signal_file} 16 | if [ "${events}" = "d" ] ; then 17 | ${service_path} -s && ${tproxy_path} -s 18 | elif [ "${events}" = "n" ] ; then 19 | ${service_path} -k && ${tproxy_path} -k 20 | fi 21 | rm ${signal_file} 22 | fi 23 | } 24 | 25 | service_control -------------------------------------------------------------------------------- /service.sh: -------------------------------------------------------------------------------- 1 | until [ $(getprop init.svc.bootanim) = "stopped" ] ; do 2 | sleep 5 3 | done 4 | 5 | service_path=`realpath $0` 6 | module_dir=`dirname ${service_path}` 7 | data_dir="/data/clash" 8 | scripts_dir="${data_dir}/scripts" 9 | Clash_data_dir="/data/clash" 10 | Clash_run_path="${Clash_data_dir}/run" 11 | Clash_pid_file="${Clash_run_path}/clash.pid" 12 | busybox_path="replace" 13 | 14 | if [ -f ${Clash_pid_file} ] ; then 15 | rm -rf ${Clash_pid_file} 16 | fi 17 | 18 | rm -rf ${Clash_run_path}/run.logs 19 | nohup ${busybox_path} crond -c ${Clash_run_path} > /dev/null 2>&1 & 20 | 21 | ${scripts_dir}/clash.service -s && ${scripts_dir}/clash.iptables -s 22 | 23 | inotifyd ${scripts_dir}/clash.inotify ${module_dir} >> /dev/null & -------------------------------------------------------------------------------- /META-INF/com/google/android/update-binary: -------------------------------------------------------------------------------- 1 | #!/sbin/sh 2 | 3 | ################# 4 | # Initialization 5 | ################# 6 | 7 | umask 022 8 | 9 | # echo before loading util_functions 10 | ui_print() { echo "$1"; } 11 | 12 | require_new_magisk() { 13 | ui_print "*******************************" 14 | ui_print " Please install Magisk v20.4+! " 15 | ui_print "*******************************" 16 | exit 1 17 | } 18 | 19 | ######################### 20 | # Load util_functions.sh 21 | ######################### 22 | 23 | OUTFD=$2 24 | ZIPFILE=$3 25 | 26 | mount /data 2>/dev/null 27 | 28 | [ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk 29 | . /data/adb/magisk/util_functions.sh 30 | [ $MAGISK_VER_CODE -lt 20400 ] && require_new_magisk 31 | 32 | install_module 33 | exit 0 34 | -------------------------------------------------------------------------------- /files.config: -------------------------------------------------------------------------------- 1 | Subcript_url="" 2 | GeoIP_dat_url="https://github.com/Loyalsoldier/geoip/releases/latest/download/cn.dat" 3 | Country_mmdb_url="https://github.com/Loyalsoldier/geoip/releases/latest/download/Country-only-cn-private.mmdb" 4 | GeoSite_url="https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat" 5 | 6 | pack_arch="arm64" # 只支持arm64, amd64 7 | 8 | # 下面的内容只在打包时使用,填写git地址 9 | pack_dashboard_repo="https://github.com/MetaCubeX/metacubexd.git" # 新的 MetaCubeXD 10 | # pack_dashboard_repo="https://github.com/MetaCubeX/Yacd-meta.git" # 旧的 YACD 11 | pack_dashboard_branch="gh-pages" 12 | 13 | # 这里配置target的文件variant 14 | 15 | mihomo_link="https://github.com/MetaCubeX/mihomo/releases" 16 | mihomo_tag="latest" # Prerelease-Alpha -> 最新测试版, latest -> 最新稳定版, 版本号 -> 指定版本 17 | mihomo_arch_arm64="android-arm64-v8" 18 | mihomo_arch_amd64="android-amd64" 19 | # 最终的下载链接为 "${mihomo_link}/${mihomo_tag}/mihomo-${arch}-${mihomo_version}.gz" 20 | # 当tag=latest时,会自动获取最新的版本号作为新的tag 21 | 22 | curl_link="https://github.com/stunnel/static-curl/releases/download" 23 | curl_version="8.6.0" 24 | # 最终的下载链接为 "${curl_link}/${curl_version}/curl-linux-${pack_arch}-${curl_version}.tar.xz" 25 | -------------------------------------------------------------------------------- /.github/workflows/mihomo-alpha.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | 4 | 5 | jobs: 6 | release: 7 | name: Release empty module 8 | 9 | runs-on: 10 | - ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | 16 | - name: Set to Alpha branch 17 | shell: bash 18 | run: | 19 | sed -i "s/mihomo_tag=.*/mihomo_tag=\"Prerelease-Alpha\"/" ./files.config 20 | 21 | - name: Build arm64 22 | shell: bash 23 | run: | 24 | sed -i "s/pack_arch=.*/pack_arch=\"arm64\"/" ./files.config 25 | bash ./pack.sh -c 26 | 27 | - name: Build amd64 28 | shell: bash 29 | run: | 30 | sed -i "s/pack_arch=.*/pack_arch=\"amd64\"/" ./files.config 31 | bash ./pack.sh -c 32 | 33 | - name: Save Artifacts ARM64 34 | uses: actions/upload-artifact@v4 35 | with: 36 | name: release-alpha-arm64 37 | path: release-arm64 38 | 39 | - name: Save Artifacts AMD64 40 | uses: actions/upload-artifact@v4 41 | with: 42 | name: release-alpha-amd64 43 | path: release-amd64 44 | 45 | - name: Save Version 46 | uses: actions/upload-artifact@v4 47 | with: 48 | name: version.txt 49 | path: version 50 | -------------------------------------------------------------------------------- /clash/scripts/updateSub.sh: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | . /data/clash/clash.config 3 | scripts=`realpath $0` 4 | scripts_dir=`dirname ${scripts}` 5 | subItem="" 6 | confPath="/data/clash/config.yaml" 7 | tempPath="/data/clash/config.new.yaml" 8 | Clash_bin_path="/data/adb/modules/Mihomo_For_Magisk/system/bin/clash" 9 | CFM_logs_file="/data/clash/run/run.logs" 10 | 11 | updateSub(){ 12 | echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"info: 更新订阅." | tee -a ${CFM_logs_file} 13 | # 验证是否设置了订阅链接 14 | if [ -z "${Subcript_url}" ]; then 15 | echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"error: 订阅链接为空,考虑关闭本功能?" | tee -a ${CFM_logs_file} 16 | return 1 17 | fi 18 | # 下载 19 | subItem=$(curl --cacert /etc/security/cacerts/cacert.pem -sL "${Subcript_url}") 20 | if [ -z "${subItem}" ]; then 21 | echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"error: 订阅链接无法获取到数据,请检查链接是否正确." | tee -a ${CFM_logs_file} 22 | return 1 23 | fi 24 | echo "${subItem}" > ${tempPath} 25 | 26 | # 使用Clash验证 27 | testResult=$(${Clash_bin_path} -t -f ${tempPath}) 28 | if [[ $testResult == *"test failed"* ]]; then 29 | echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"error: 订阅链接无法通过Clash验证,请检查链接是否正确." | tee -a ${CFM_logs_file} 30 | return 1 31 | fi 32 | # 替换 33 | mv -f ${tempPath} ${confPath} 34 | echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"info: 订阅下载成功." | tee -a ${CFM_logs_file} 35 | } 36 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | ## 1.4.2-1 3 | - clash.internal.config -> keep_dns 弃用 4 | - 不使用此功能没有必要更新 5 | ## 1.4.2 6 | - 合并 dns 修复 7 | - 现在 template 没有 dns 就会自动从 config.yaml 加 8 | - template 加上 '#keep' 即可在更新保留 9 | - 快看看新 README 10 | ## 1.4.1 11 | - internal: 可以合并的时候保留 dns (clash.internal.config -> keep_dns) 12 | - 模块列表可以看到模块检测到的环境类型 13 | ## 1.4.0 14 | - 尝试支持 KSU/Apatch 15 | - 分离 clash.config 适合修改和不适合修改的逻辑 16 | ## 1.3.2 17 | - 添加 `action.sh`,现在可以在 magisk 里面点开 webui 18 | ## 1.3.1 19 | - 继续优化 template,细分 template DNS 20 | - template DNS 减少使用 AliDNS 解决可能的严重限速问题 21 | - 现在更多信息会打印到 `stdout` 22 | ## 1.3.0 23 | - 使用更新的 [MetacubeXD](https://github.com/MetaCubeX/metacubexd) 作为 dashboard 24 | - template 默认的 DNS 加强,减少被劫持的可能 25 | ## 1.2.8-1 26 | - 修改默认`template`, 修改错误的`fallback-filter`,修改默认 DNS 服务器 27 | ## 1.2.8 28 | - 修复iptables处理逻辑 29 | ## 1.2.7 30 | - 现在会发布到 Telegram Channel 31 | - 修复CI打包的逻辑错误 32 | ## 1.2.6 33 | - 修复错误 34 | - 更新默认template 35 | ## 1.2.5 36 | - 修复错误 37 | ## 1.2.4 38 | - 优化更新`config.yaml`体验,现在理应只需要一次`/data/clash/scripts/clash.tool -s` 39 | ## 1.2.3 40 | - 现在会更新cacert.pem了(需要 curl `7.68.0` 以上以支持etag相关功能) 41 | - 修复typo(欢迎大家继续找typo等) 42 | ## 1.2.2 43 | - 修复上个版本的bug 44 | ## 1.2.1 45 | - 现在Magisk管理页面会有内核版本了 46 | - 修复typo导致不复制provider 47 | ## 1.2.0 48 | - 现在 Github Actions 可以自动打包,可以直接安装 49 | ## 1.1.1 50 | - 优化了输出的文件名,现在不会默认删除release文件夹了 51 | ## 1.1.0 52 | - 现在可以打包amd64了 53 | - 现在可以自定义更多内容了 54 | - 为新的自定义增加了简单的手动检查功能 55 | ## 1.0.3 56 | - 现在安装时如果打包的是空的`config.yaml`,则会从上一个安装中复制 57 | - 修好了更新订阅的功能 58 | - 修复了默认`files.config`把curl`amd64`看成`arm64`的弱智bug 59 | ## 1.0.2 60 | - 优化了安装时的逻辑 61 | - 现在安装时可以知道`原神`版本 62 | - 现在打包时下载providers需要python3,解决了可能的打包失败以及原来逻辑可能出现的错误 63 | - 现在不检查打包完整性,因为理应不可以有不完整的包 64 | ## 1.0.1 65 | - 修复了部分情况下打包失败的问题 66 | ## 1.0.0 67 | - 初步完成了可用的版本 -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - CHANGELOG.md 9 | 10 | jobs: 11 | release: 12 | name: Release empty module 13 | runs-on: 14 | - ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | 19 | - name: Build arm64 20 | shell: bash 21 | run: | 22 | sed -i "s/pack_arch=.*/pack_arch=\"arm64\"/" ./files.config 23 | bash ./pack.sh -c 24 | 25 | - name: Build amd64 26 | shell: bash 27 | run: | 28 | sed -i "s/pack_arch=.*/pack_arch=\"amd64\"/" ./files.config 29 | bash ./pack.sh -c 30 | 31 | - name: Save Artifacts ARM64 32 | uses: actions/upload-artifact@v4 33 | with: 34 | name: release-arm64 35 | path: release-arm64 36 | 37 | - name: Save Artifacts AMD64 38 | uses: actions/upload-artifact@v4 39 | with: 40 | name: release-amd64 41 | path: release-amd64 42 | 43 | - name: Save Version 44 | uses: actions/upload-artifact@v4 45 | with: 46 | name: version.txt 47 | path: version 48 | 49 | - name: Upload to Telegram Channel 50 | env: 51 | CHANNEL: ${{ secrets.CHANNEL }} 52 | BOT_TOKEN: ${{ secrets.BOT_TOKEN }} 53 | run: | 54 | VERSION=$(cat ./version | awk -F 'v' '{print $2}') 55 | OUT_DIR="release/" 56 | export arm64=$(find $OUT_DIR -name "MFM-arm64-v*.zip") 57 | export caption=$(echo -e "New Release of arm64\nmihomo=${VERSION}\nCommit SHA=${{ github.sha }}\nCommit Message:\n ${{ github.event.head_commit.message }}" | jq -Rsr @uri ) 58 | if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then 59 | curl -s -X POST \ 60 | -H "Content-Type:multipart/form-data"\ 61 | -F document=@"$arm64" \ 62 | "https://api.telegram.org/bot${BOT_TOKEN}/sendDocument?chat_id=${CHANNEL}&caption=${caption}" 63 | fi 64 | 65 | -------------------------------------------------------------------------------- /.github/workflows/dev.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | 4 | push: 5 | branches: 6 | - dev 7 | 8 | jobs: 9 | release: 10 | name: Release empty module 11 | runs-on: 12 | - ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | 17 | - name: Build arm64 18 | shell: bash 19 | run: | 20 | sed -i "s/pack_arch=.*/pack_arch=\"arm64\"/" ./files.config 21 | bash ./pack.sh -c 22 | 23 | - name: Build amd64 24 | shell: bash 25 | run: | 26 | sed -i "s/pack_arch=.*/pack_arch=\"amd64\"/" ./files.config 27 | bash ./pack.sh -c 28 | 29 | - name: Save Artifacts ARM64 30 | uses: actions/upload-artifact@v4 31 | with: 32 | name: release-arm64 33 | path: release-arm64 34 | 35 | - name: Save Artifacts AMD64 36 | uses: actions/upload-artifact@v4 37 | with: 38 | name: release-amd64 39 | path: release-amd64 40 | 41 | - name: Save Version 42 | uses: actions/upload-artifact@v4 43 | with: 44 | name: version.txt 45 | path: version 46 | 47 | - name: Upload to Telegram Channel 48 | env: 49 | CHANNEL: ${{ secrets.CHANNEL }} 50 | BOT_TOKEN: ${{ secrets.BOT_TOKEN }} 51 | run: | 52 | VERSION=$(cat ./version | awk -F 'v' '{print $2}') 53 | OUT_DIR="release/" 54 | export arm64=$(find $OUT_DIR -name "MFM-arm64-v*.zip") 55 | export caption=$(echo -e "New Dev Release of arm64\nmihomo=${VERSION}\nCommit SHA=${{ github.sha }}\nCommit Message:\n${{ github.event.head_commit.message }}\nUSE WITH CAUTION!\n开发版本,小心使用!" | jq -Rsr @uri ) 56 | if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then 57 | curl -s -X POST \ 58 | -H "Content-Type:multipart/form-data"\ 59 | -F document=@"$arm64" \ 60 | "https://api.telegram.org/bot${BOT_TOKEN}/sendDocument?chat_id=${CHANNEL}&disable_notification=true&caption=${caption}" 61 | fi 62 | 63 | -------------------------------------------------------------------------------- /download_providers.py: -------------------------------------------------------------------------------- 1 | import os 2 | import requests 3 | import yaml 4 | 5 | def fix_t(file:str): 6 | ''' 7 | 修复文件中`\t`会使得yaml读取失败的问题 8 | ''' 9 | with open(file, 'r', encoding='utf-8') as f: 10 | content = f.read() 11 | content = content.replace('\t', ' ') 12 | with open(file, 'w', encoding='utf-8') as f: 13 | f.write(content) 14 | 15 | 16 | def download_provider(folder:str): 17 | ''' 18 | 下载的folder中的provider的url至path。 19 | Args: 20 | folder: clash运行配置所在文件夹的绝对路径 21 | config.yaml样例: 22 | ```yaml 23 | proxy-providers: 24 | A: 25 | type: http 26 | url: "https://example.com/A.yaml" 27 | path: ./proxy_providers/A.yaml 28 | ``` 29 | 会将A.yaml下载至 ./clash/proxy_providers/A.yaml 30 | ''' 31 | if not os.path.exists(folder): 32 | print(f"{folder} 不存在") 33 | return 34 | conf_file = os.path.join(folder, 'config.yaml') 35 | fix_t(conf_file) 36 | 37 | with open(conf_file, 'r', encoding='utf-8') as f: 38 | config = yaml.safe_load(f) 39 | try: 40 | proxy_providers = config['proxy-providers'] 41 | for _,v in proxy_providers.items(): 42 | url = v['url'] 43 | path = v['path'] 44 | if v['type']=="http": 45 | destination = os.path.join(folder,path[2:]) 46 | print(f"正在下载至 {destination}") 47 | r = requests.get(url) 48 | with open(destination, 'wb') as f: 49 | f.write(r.content) 50 | print(f"下载成功") 51 | except KeyError: 52 | print("没有proxy-providers,跳过") 53 | except TypeError: 54 | print("config.yaml无yaml结构") 55 | return 56 | 57 | try: 58 | rule_providers = config['rule-providers'] 59 | for _,v in rule_providers.items(): 60 | url = v['url'] 61 | path = v['path'] 62 | if v['type']=="http": 63 | destination = os.path.join(folder,path[2:]) 64 | print(f"正在下载至 {destination}") 65 | r = requests.get(url) 66 | with open(destination, 'wb') as f: 67 | f.write(r.content) 68 | print(f"下载成功") 69 | except KeyError: 70 | print("没有rule-providers,跳过") 71 | 72 | if __name__=="__main__": 73 | 74 | if os.sys.argv.__len__()>1: 75 | download_provider(os.sys.argv[1]) 76 | else: 77 | download_provider('clash') -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mihomo For Magisk 2 | 3 | ![Repository Overview](https://socialify.git.ci/icewithcola/Mihomo_For_Magisk/image?description=1&forks=1&issues=1&name=1&owner=1&pulls=1&stargazers=1&theme=Dark) 4 | 5 | Mihomo For Magisk 是一个适用于 Magisk/KSU/Apatch 的 Mihomo 模块,旨在提供 Mihomo 相关功能的便捷支持,使其能够在 Android 设备上更高效地运行。 6 | 7 | ## 📥 下载 8 | - [![CI Build](https://github.com/icewithcola/Mihomo_For_Magisk/actions/workflows/main.yml/badge.svg)](https://github.com/icewithcola/Mihomo_For_Magisk/actions/workflows/main.yml) 9 | - [![Telegram](https://img.shields.io/static/v1?label=Telegram&message=@mfm_updates&color=5BCEFA)](https://t.me/mfm_updates) 10 | 11 | ## 🌟 主要特性 12 | - **一次手动打包,后续支持空包更新** 13 | - **自动下载 Mihomo 运行所需资源** (如 GeoX, MMDB, Dashboard) 14 | - **可选手动打包**,自动下载附加资源(包括 `config.yaml`、`proxy_providers`、`rule_providers`) 15 | - **运行时动态更新 `config.yaml`**,保证配置的实时性 16 | 17 | ## 🔧 打包方式 18 | 在 Linux 终端运行以下命令进行打包: 19 | 20 | ```sh 21 | ./pack.sh -a 22 | ``` 23 | 24 | ⚠️ 需要以下依赖:`gzip`, `wget`, `curl`, `python3`。 25 | 26 | ## 🛠 自定义配置 27 | ### 📄 files.config 28 | 该文件用于存储打包过程中的变量配置,具体内容可参考文件内的注释。 29 | 30 | | 变量名 | 对应值 | 说明 | 31 | |--------|--------|------| 32 | | `pack_arch` | `mihomo_arch_arm64` | 默认为 `android-arm64-v8` | 33 | | `pack_arch` | `mihomo_arch_amd64` | 默认为 `android-amd64`,但 **未经过测试** | 34 | 35 | ⚠️ `amd64` 版本未经过测试(因缺少设备),已知 AVD 上需要 `linux-amd64-compatible`。 36 | 37 | #### 📌 版本选择 (`mihomo_tag`) 38 | | 版本值 | 说明 | 39 | |--------|------| 40 | | `latest` | 指向最新稳定版本 | 41 | | `Prerelease-Alpha` | 指向最新测试版 | 42 | | `v1.1.4` (示例) | 指向指定版本 | 43 | 44 | ⚠️ `pack_arch` 在不同版本之间 **可能不相同**。 45 | 46 | 🔹 `curl_version` 允许手动指定 `curl` 的版本,但某些特殊格式 (`x.y.z-k`) 可能无法正确获取下载链接。 47 | 48 | ### ⚠️ 注意事项 49 | 更新 `files.config` 中的下载链接时,请同时更新 `./clash/clash.config` 里的相同部分(如果适用)。 50 | 51 | ⚠️ **仅在自用时填写订阅链接,否则您的订阅信息可能会被打包进模块,造成泄露风险!** 52 | 53 | ## 🎛 Clash 配置 54 | ### 📁 clash/template 55 | - 在 `template` 文件的开头添加 `#keep`,可防止更新时被覆盖。 56 | - 如果 `template` 中 **没有** `dns` 块,则会从 `config.yaml` 的 `dns` 开始合并。 57 | - 否则,将从 `config.yaml` 的 `proxy:` 行开始合并,因此 **请将其他需要合并的内容放在 `proxy` 下方**。 58 | 59 | 🔹 **最终合并方式示例**: 60 | ```yaml 61 | template 中的内容 ... 62 | config.yaml proxy:/dns: 这一行以下的内容 ... 63 | ``` 64 | 65 | ## ⚡️ 操作指南 66 | ### ✅ 启动 Mihomo 67 | ```sh 68 | /data/clash/scripts/clash.service -s 69 | ``` 70 | 71 | ### ❌ 关闭 Mihomo 72 | ```sh 73 | /data/clash/scripts/clash.service -k 74 | ``` 75 | 76 | ### 🔄 更新订阅 77 | ```sh 78 | /data/clash/scripts/clash.tool -s 79 | ``` 80 | ⚠️ **可能需要多次尝试并手动重启,确保更新成功。** 81 | 82 | 🤔 在 Magisk 中更新模块后,可以通过重启 mihomo 完成更新,而无需重启 83 | 84 | ### 🌐 WebUI 访问 85 | 访问 [http://127.0.0.1:9090/ui](http://127.0.0.1:9090/ui) 或在 Magisk 模块列表中点击 **操作** 打开。 86 | 87 | 🔹 **默认仅允许本机访问**,如需远程访问,请修改 `template` 并添加 `#keep`,建议同时设置 `secret` 以增强安全性。 88 | 89 | ## 🔗 相关链接 90 | - [Mihomo 官方仓库](https://github.com/MetaCubeX/mihomo) 91 | - [Magisk 官方仓库](https://github.com/topjohnwu/Magisk) 92 | - [Clash for Magisk (CFM)](https://github.com/taamarin/ClashforMagisk) 93 | 94 | --- 95 | 📢 **欢迎贡献代码,提出 Issue,或加入 Telegram 讨论!** 🚀 -------------------------------------------------------------------------------- /clash/clash.internal.config: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | 3 | # 此文件在正常情况下不需要修改,请勿在运行时修改 4 | # There are no need to change this file in most cases, do not edit when clash is running 5 | 6 | ipv6="true" 7 | pref_id="5000" 8 | mark_id="2021" 9 | table_id="2021" 10 | mode="blacklist" 11 | static_dns="8.8.8.8" 12 | Clash_bin_name="clash" 13 | Clash_data_dir="/data/clash" 14 | Clash_run_path="${Clash_data_dir}/run" 15 | CFM_logs_file="${Clash_run_path}/run.logs" 16 | template_file="${Clash_data_dir}/template" 17 | appuid_file="${Clash_run_path}/appuid.list" 18 | Clash_pid_file="${Clash_run_path}/clash.pid" 19 | Clash_bin_path="/data/adb/modules/Mihomo_For_Magisk/system/bin/${Clash_bin_name}" 20 | Clash_config_file="${Clash_data_dir}/config.yaml" 21 | system_packages_file="/data/system/packages.list" 22 | temporary_config_file="${Clash_run_path}/config.yaml" 23 | filter_packages_file="${Clash_data_dir}/packages.list" 24 | Clash_GeoSite_file="${Clash_data_dir}/GeoSite.dat" 25 | geodata_mode=$(grep "geodata-mode" ${template_file} | awk -F ': ' '{print $2}') 26 | if "${geodata_mode}"; then 27 | Clash_GeoIP_file="${Clash_data_dir}/GeoIP.dat" 28 | GeoIP_url=${GeoIP_dat_url} 29 | else 30 | Clash_GeoIP_file="${Clash_data_dir}/Country.mmdb" 31 | GeoIP_url=${Country_mmdb_url} 32 | fi 33 | 34 | # 找一个 busybox 35 | busybox_path="replace" 36 | 37 | # 自动绕过本机ip,filter_local请勿轻易打开,打开后有可能引起设备软重启,如你手机有获取到公网ip. 38 | # 优先重启cfm,即可绕过本机ip,检查是否正常,次要选择尝试打开filter_local,如遇设备软重启,请关闭. 39 | filter_local="false" 40 | #请不要轻易打开. 不要轻易打开,不要轻易打开. 41 | 42 | Cgroup_memory_path="" 43 | # 留空则自动获取 44 | Cgroup_memory_limit="" 45 | # 限制内存使用,量力而行,限制太死会造成CPU占用过高,-1则不限制,留空则不操作 46 | # 更新限制请保存后执行 /data/clash/scripts/clash.tool -l 47 | Clash_permissions="6755" 48 | Clash_user_group="root:net_admin" 49 | iptables_wait="iptables -w 100" 50 | ip6tables_wait="ip6tables -w 100" 51 | Clash_user=$(echo ${Clash_user_group} | awk -F ':' '{print $1}') 52 | Clash_group=$(echo ${Clash_user_group} | awk -F ':' '{print $2}') 53 | Clash_tproxy_port=$(grep "tproxy-port" ${template_file} | awk -F ': ' '{print $2}') 54 | 55 | # Configure DNS 56 | Clash_dns_port=$(grep "listen" ${template_file} | awk -F ':' '{print $3}') 57 | Clash_enhanced_mode=$(grep "enhanced-mode" ${template_file} | awk -F ': ' '{print $2}') 58 | 59 | if [ -z "${Clash_dns_port}" ]; then 60 | Clash_dns_port=$(grep "listen" ${Clash_config_file} | awk -F ':' '{print $3}') 61 | fi 62 | 63 | if [ -z "${Clash_enhanced_mode}" ]; then 64 | Clash_enhanced_mode=$(grep "enhanced-mode" ${Clash_config_file} | awk -F ': ' '{print $2}') 65 | fi 66 | # end 67 | # Configure TUN 68 | Clash_tun_status=$(awk -F ': ' '/^tun: *$/{getline; print $2}' ${template_file}) 69 | Clash_auto_route=$(grep "auto-route" ${template_file} | awk -F ': ' '{print $2}') 70 | tun_device=$(awk -F ': ' '/ +device: /{print $2}' ${template_file}) 71 | 72 | if [ "${Clash_tun_status}" == "" ]; then 73 | Clash_tun_status="false" 74 | fi 75 | if [ "${Clash_auto_route}" == "" ]; then 76 | Clash_auto_route="false" 77 | fi 78 | if [ "${tun_device}" == "" ]; then 79 | tun_device="Meta" 80 | fi 81 | 82 | reserved_ip=(0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.0.0.0/24 192.0.2.0/24 192.168.0.0/16 198.51.100.0/24 203.0.113.0/24 224.0.0.0/4 255.255.255.255/32 240.0.0.0/4) 83 | reserved_ip6=(::/128 ::1/128 ::ffff:0:0/96 100::/64 64:ff9b::/96 2001::/32 2001:10::/28 2001:20::/28 2001:db8::/32 2002::/16 fc00::/7 fe80::/10 ff00::/8) 84 | -------------------------------------------------------------------------------- /clash/template: -------------------------------------------------------------------------------- 1 | tproxy-port: 7893 2 | allow-lan: true 3 | geodata-mode: true 4 | unified-delay: true 5 | mode: rule 6 | log-level: info 7 | ipv6: true 8 | tcp-concurrent: false 9 | sniffer: 10 | enable: false 11 | profile: 12 | store-fake-ip: true 13 | external-controller: 127.0.0.1:9090 14 | external-ui: clash-dashboard/dist 15 | 16 | tun: 17 | enable: false 18 | device: Meta 19 | stack: system # gvisor 20 | dns-hijack: 21 | - any:53 22 | auto-route: true 23 | auto-detect-interface: true 24 | 25 | dns: 26 | enable: true 27 | listen: 0.0.0.0:1053 28 | ipv6: true 29 | # DNS 30 | default-nameserver: # For resolving DNS's ip 31 | - tls://223.5.5.5:853 # alidns 32 | - tls://120.53.53.53 # dnspod 33 | nameserver: 34 | - https://dns.alidns.com/dns-query 35 | - https://cloudflare-dns.com/dns-query 36 | - https://dns.adguard.com/dns-query 37 | fallback: 38 | - https://dns.google/dns-query 39 | - https://doh.opendns.com/dns-query 40 | respect-rules: true 41 | proxy-server-nameserver: # For proxy server's ip 42 | - 180.184.1.1 # Bytedance DNS 43 | - 119.29.29.29 # dnspod 44 | 45 | 46 | # FakeIP 47 | enhanced-mode: fake-ip 48 | fake-ip-range: 198.18.0.1/16 49 | fake-ip-filter: 50 | - "*.lan" 51 | - stun.*.*.* 52 | - stun.*.* 53 | - time.windows.com 54 | - time.nist.gov 55 | - time.apple.com 56 | - time.asia.apple.com 57 | - "*.ntp.org.cn" 58 | - "*.openwrt.pool.ntp.org" 59 | - time1.cloud.tencent.com 60 | - time.ustc.edu.cn 61 | - pool.ntp.org 62 | - ntp.ubuntu.com 63 | - ntp.aliyun.com 64 | - ntp1.aliyun.com 65 | - ntp2.aliyun.com 66 | - ntp3.aliyun.com 67 | - ntp4.aliyun.com 68 | - ntp5.aliyun.com 69 | - ntp6.aliyun.com 70 | - ntp7.aliyun.com 71 | - time1.aliyun.com 72 | - time2.aliyun.com 73 | - time3.aliyun.com 74 | - time4.aliyun.com 75 | - time5.aliyun.com 76 | - time6.aliyun.com 77 | - time7.aliyun.com 78 | - "*.time.edu.cn" 79 | - time1.apple.com 80 | - time2.apple.com 81 | - time3.apple.com 82 | - time4.apple.com 83 | - time5.apple.com 84 | - time6.apple.com 85 | - time7.apple.com 86 | - time1.google.com 87 | - time2.google.com 88 | - time3.google.com 89 | - time4.google.com 90 | - music.163.com 91 | - "*.music.163.com" 92 | - "*.126.net" 93 | - musicapi.taihe.com 94 | - music.taihe.com 95 | - songsearch.kugou.com 96 | - trackercdn.kugou.com 97 | - "*.kuwo.cn" 98 | - api-jooxtt.sanook.com 99 | - api.joox.com 100 | - joox.com 101 | - y.qq.com 102 | - "*.y.qq.com" 103 | - streamoc.music.tc.qq.com 104 | - mobileoc.music.tc.qq.com 105 | - isure.stream.qqmusic.qq.com 106 | - dl.stream.qqmusic.qq.com 107 | - aqqmusic.tc.qq.com 108 | - amobile.music.tc.qq.com 109 | - "*.xiami.com" 110 | - "*.music.migu.cn" 111 | - music.migu.cn 112 | - "*.msftconnecttest.com" 113 | - "*.msftncsi.com" 114 | - localhost.ptlogin2.qq.com 115 | - "*.*.*.srv.nintendo.net" 116 | - "*.*.stun.playstation.net" 117 | - xbox.*.*.microsoft.com 118 | - "*.ipv6.microsoft.com" 119 | - "*.*.xboxlive.com" 120 | - speedtest.cros.wr.pvp.net 121 | - "*.d.meituan.net" 122 | # Fallback 123 | fallback-filter: 124 | geoip: true 125 | geoip-code: CN 126 | ipcidr: 127 | - 240.0.0.0/4 128 | - 0.0.0.0/32 129 | - 127.0.0.1/32 130 | # Antifraud 131 | - 61.160.148.90/32 132 | - 124.236.16.201/32 133 | - 182.43.124.6/32 134 | - 106.74.25.198/32 135 | - 120.222.19.204/32 136 | - 221.228.32.13/32 137 | - 211.137.117.149/32 138 | - 36.135.82.110/32 139 | - 183.203.36.36/32 140 | - 111.28.0.32/32 141 | - 117.156.25.11/32 142 | - 120.204.204.201/32 143 | - 120.209.204.204/32 144 | - 211.138.218.190/32 145 | - 183.252.183.9/32 146 | - 117.187.10.42/32 147 | - 218.203.122.98/32 148 | - 221.180.160.221/32 149 | - 111.31.192.110/32 150 | - 221.130.39.3/32 151 | - 120.209.204.20/32 152 | - 218.201.25.129/32 153 | - 211.139.145.129/32 154 | - 36.138.129.47/32 155 | - 112.54.50.50/32 156 | - 111.22.226.1/32 157 | - 111.63.132.170/32 158 | - 111.44.246.131/32 159 | - 112.17.14.211/32 160 | - 111.22.226.1/32 161 | - 47.92.198.202/32 162 | domain: 163 | - +.google.com 164 | - +.facebook.com 165 | - +.twitter.com 166 | - +.youtube.com 167 | - +.xn--ngstr-lra8j.com 168 | - +.google.cn 169 | - +.googleapis.cn 170 | - +.googleapis.com 171 | - +.gvt1.com 172 | - +.duckduckgo.com 173 | 174 | 175 | -------------------------------------------------------------------------------- /customize.sh: -------------------------------------------------------------------------------- 1 | SKIPUNZIP=1 2 | 3 | status="" 4 | architecture="" 5 | system_gid="1000" 6 | system_uid="1000" 7 | clash_data_dir="/data/clash" 8 | modules_dir="/data/adb/modules" 9 | ca_path="/system/etc/security/cacerts" 10 | mod_config="${clash_data_dir}/clash.config" 11 | geoip_file_path="${clash_data_dir}/Country.mmdb" 12 | target_arch="arm64" 13 | 14 | check_env(){ 15 | # 检测架构 16 | if [ "${ARCH}" -ne "${target_arch}" ]; then 17 | abort "不支持的架构" 18 | fi 19 | 20 | # 检测环境变量 21 | if [ -z ${MODPATH} ]; then 22 | abort "检测到环境变量问题,请检查Magisk" 23 | fi 24 | if [ -z ${clash_data_dir} ] ; then 25 | abort "检测到环境变量问题,请检查Magisk" 26 | fi 27 | if [ -z ${ZIPFILE} ]; then 28 | abort "检测到环境变量问题,请检查Magisk" 29 | fi 30 | } 31 | 32 | check_lastinstall(){ 33 | # 检测是否已经安装过 34 | if [ -d "${clash_data_dir}" ]; then 35 | ui_print "检测到已安装过的Clash,将您的配置文件迁移到新的目录." 36 | if [ -d "${clash_data_dir}.old" ]; then 37 | rm -rf "${clash_data_dir}.old" 38 | fi 39 | mv -f ${clash_data_dir} ${clash_data_dir}.old 40 | set_perm_recursive ${clash_data_dir}.old ${system_uid} ${system_gid} 0755 0644 # 修复权限 41 | fi 42 | } 43 | 44 | release_file(){ 45 | mkdir -p ${MODPATH}/system/bin 46 | mkdir -p ${clash_data_dir} 47 | mkdir -p ${MODPATH}${ca_path} 48 | 49 | mkdir -p ${clash_data_dir}/proxy_providers 50 | mkdir -p ${clash_data_dir}/rule_providers 51 | 52 | unzip -o "${ZIPFILE}" -x 'META-INF/*' -d $MODPATH >&2 53 | ui_print $(cat $MODPATH/version) 54 | mkdir ${clash_data_dir} 55 | 56 | mv ${MODPATH}/binary/* ${MODPATH}/system/bin 57 | mv ${MODPATH}/cacert.pem ${MODPATH}${ca_path} 58 | mv -n ${MODPATH}/clash-dashboard ${clash_data_dir}/ 59 | rm -rf ${MODPATH}/clash-dashboard 60 | mv -f ${MODPATH}/clash/* ${clash_data_dir}/ 61 | rm -rf ${MODPATH}/clash 62 | rm -rf ${MODPATH}/binary 63 | 64 | if [ ! -f "${clash_data_dir}/packages.list" ] ; then 65 | touch ${clash_data_dir}/packages.list 66 | fi 67 | } 68 | 69 | move_config(){ 70 | if [ ! -s "${clash_data_dir}/config.yaml" ] && [ -s "${clash_data_dir}.old/config.yaml" ]; then 71 | echo "在旧的安装中找到可用的config.yaml等配置,将其迁移到新的目录." 72 | cp -f ${clash_data_dir}.old/config.yaml ${clash_data_dir}/config.yaml 73 | cp -f ${clash_data_dir}.old/clash.config ${clash_data_dir}/clash.config 74 | cp -f ${clash_data_dir}.old/proxy_providers/* ${clash_data_dir}/proxy_providers/ 75 | cp -f ${clash_data_dir}.old/rule_providers/* ${clash_data_dir}/rule_providers/ 76 | 77 | if grep -q '^#keep' ${clash_data_dir}.old/template; then 78 | cp -f ${clash_data_dir}.old/template ${clash_data_dir}/template 79 | echo "保留 template" 80 | fi 81 | fi 82 | } 83 | 84 | config_compatible(){ # 所有兼容性修改都在这里 85 | # 1.4.0 分离 clash.config 内外逻辑 86 | config_file="${clash_data_dir}/clash.config" 87 | sed -i '/# 上面的是给你操作的,下面的不懂就别乱改/,$d' "$config_file" 2>/dev/null 88 | if [ -f "$config_file" ] && ! grep -q "\. /data/clash/clash.internal.config" "$file"; then 89 | sed -i '2i\. /data/clash/clash.internal.config' "$config_file" 90 | fi 91 | } 92 | 93 | setup_perm(){ 94 | ui_print "- 开始设置环境权限." 95 | set_perm_recursive ${MODPATH} 0 0 0755 0644 96 | set_perm ${MODPATH}/system/bin/setcap 0 0 0755 97 | set_perm ${MODPATH}/system/bin/getcap 0 0 0755 98 | set_perm ${MODPATH}/system/bin/getpcaps 0 0 0755 99 | set_perm ${MODPATH}${ca_path}/cacert.pem 0 0 0644 100 | set_perm ${MODPATH}/system/bin/curl 0 0 0755 101 | set_perm_recursive ${clash_data_dir} ${system_uid} ${system_gid} 0755 0644 102 | set_perm_recursive ${clash_data_dir}/scripts ${system_uid} ${system_gid} 0755 0755 103 | set_perm ${MODPATH}/system/bin/clash ${system_uid} ${system_gid} 6755 104 | set_perm ${clash_data_dir}/clash.config ${system_uid} ${system_gid} 0755 105 | set_perm ${clash_data_dir}/clash.internal.config ${system_uid} ${system_gid} 0755 106 | set_perm ${clash_data_dir}/packages.list ${system_uid} ${system_gid} 0644 107 | } 108 | 109 | setup_busybox(){ 110 | if [ "${KSU}" ]; then 111 | ui_print "Setting up for KSU..." 112 | setup_busybox_internal "/data/adb/ksu/bin/busybox" 113 | sed -i "s|KSU|KSU✅|g" "$MODPATH/module.prop" 114 | elif [ "${APATCH}" ]; then 115 | ui_print "Setting up for Apatch..." 116 | setup_busybox_internal "/data/adb/ap/bin/busybox" 117 | sed -i "s|APatch|APatch✅|g" "$MODPATH/module.prop" 118 | 119 | else 120 | ui_print "Setting up for Magisk or unknown environment..." 121 | setup_busybox_internal "/data/adb/magisk/busybox" 122 | sed -i "s|Magisk/|Magisk✅/|g" "$MODPATH/module.prop" 123 | fi 124 | } 125 | 126 | setup_busybox_internal() { 127 | if [ -z "$1" ]; then 128 | ui_print "$0 " 129 | abort 130 | fi 131 | 132 | busybox_path="$1" 133 | ui_print "Busybox at $1" 134 | files="$MODPATH/service.sh $clash_data_dir/clash.internal.config" 135 | 136 | for file in $files; do 137 | if [ -f "$file" ]; then 138 | ui_print "Replacing in $file" 139 | sed -i "s|busybox_path=\"replace\"|busybox_path=\"$busybox_path\"|g" "$file" 140 | fi 141 | done 142 | } 143 | 144 | 145 | check_env 146 | check_lastinstall 147 | release_file 148 | move_config 149 | setup_busybox 150 | config_compatible 151 | setup_perm -------------------------------------------------------------------------------- /pack.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | set -e 4 | 5 | . ./files.config 6 | 7 | 8 | # 下载文件 9 | download_binaries(){ 10 | mkdir -p ./binary 11 | if [ -f ./version ]; then 12 | rm ./version 13 | fi 14 | touch ./version 15 | 16 | # mihomo 17 | echo "正在下载mihomo..." 18 | 19 | if [ "$pack_arch" == "arm64" ]; then 20 | arch=$mihomo_arch_arm64 21 | else 22 | if [ "$pack_arch" == "amd64" ]; then 23 | arch=$mihomo_arch_amd64 24 | else 25 | echo "不支持的打包架构: ${pack_arch}" 26 | exit 1 27 | fi 28 | fi 29 | 30 | if [ "$mihomo_tag" == "latest" ]; then 31 | mihomo_tag=$(curl -sL "${mihomo_link}/latest/download/version.txt") 32 | fi 33 | mihomo_version=$(curl -sL "${mihomo_link}/download/${mihomo_tag}/version.txt") 34 | echo "mihomo版本:${mihomo_version}" >> ./version 35 | wget -q --show-progress "${mihomo_link}/download/${mihomo_tag}/mihomo-${arch}-${mihomo_version}.gz" -O mihomo.gz 36 | gunzip mihomo.gz 37 | mv "mihomo" ./binary/clash 38 | chmod 0755 ./binary/clash 39 | rm -f mihomo.gz 40 | 41 | # curl 42 | echo "正在下载curl..." 43 | wget -q --show-progress "${curl_link}/${curl_version}/curl-linux-${pack_arch}-${curl_version}.tar.xz" -O curl.tar.xz 44 | tar -xvf curl.tar.xz 45 | mv curl ./binary/curl 46 | chmod 0755 ./binary/curl 47 | rm -f curl.tar.xz 48 | 49 | # 现在打包时下载最新的cacert.pem 50 | curl --etag-compare cacert-etag.txt --etag-save cacert-etag.txt --remote-name https://curl.se/ca/cacert.pem 51 | 52 | 53 | #修改customize.sh 54 | if [ "$pack_arch" == "amd64" ]; then 55 | sed -i "s/target_arch=.*/target_arch=\"x64\"/" ./customize.sh 56 | else 57 | sed -i "s/target_arch=.*/target_arch=\"$pack_arch\"/" ./customize.sh 58 | fi 59 | 60 | } 61 | 62 | download_geoX(){ 63 | # GeoIP 64 | echo "正在下载GeoIP..." 65 | wget -q --show-progress "${GeoIP_dat_url}" -O ./clash/GeoIP.dat 66 | 67 | # Country.mmdb 68 | echo "正在下载Country.mmdb..." 69 | wget -q --show-progress "${Country_mmdb_url}" -O ./clash/Country.mmdb 70 | 71 | # GeoSite 72 | echo "正在下载GeoSite..." 73 | wget -q --show-progress "${GeoSite_url}" -O ./clash/GeoSite.dat 74 | } 75 | 76 | 77 | download_config(){ 78 | if [ -z "${Subcript_url}" ]; then 79 | echo "订阅链接为空,跳过" 80 | else 81 | echo "正在下载config.yaml..." 82 | wget -q --show-progress "${Subcript_url}" -O ./clash/config.yaml 83 | fi 84 | 85 | # 需要安装python3 86 | if ! [ -x "$(command -v python3)" ]; then 87 | echo "请安装python3以自动下载Provider" 88 | return 89 | fi 90 | mkdir -p ./clash/proxy_providers 91 | mkdir -p ./clash/rule_providers 92 | 93 | python3 ./download_providers.py "$(dirname "$0")/clash" 94 | } 95 | 96 | download_dashboard(){ 97 | if [ -z "${pack_dashboard_repo}" ]; then 98 | echo "Dashboard链接为空,跳过" 99 | return 100 | fi 101 | 102 | if [ -d ./clash-dashboard ]; then 103 | rm -rf ./clash-dashboard 104 | fi 105 | 106 | echo "正在下载dashboard..." 107 | mkdir -p ./clash-dashboard 108 | git clone ${pack_dashboard_repo} -b ${pack_dashboard_branch} ./clash-dashboard/dist 109 | rm -rf ./clash-dashboard/dist/.git 110 | } 111 | 112 | 113 | pack(){ 114 | # 给module.prop加入版本号 115 | version=$(cat ./version | awk -F 'v' '{print $2}') 116 | sed -i "s/mihomo版本:.*/mihomo版本: $version/" ./module.prop 117 | 118 | echo "开始打包..." 119 | 120 | mkdir -p ./release 121 | 122 | filename="MFM-${pack_arch}-`cat ./version | awk -F ':' '{print $2}'`.zip" 123 | zip -r $filename -q . -x "pack.sh" "files.config" "download_providers.py" "release/*" ".git/*" ".gitignore" ".github/*" "cacert-etag.txt" "release-amd64/*" "release-arm64/*" @ 124 | mv -f $filename ./release/$filename 125 | md5sum ./release/$filename > ./release/$filename.md5 126 | 127 | echo "打包完成" 128 | } 129 | 130 | test_pack(){ 131 | echo "正在测试mihomo链接是否可用..." 132 | if [ "$mihomo_tag" == "latest" ]; then 133 | mihomo_tag=$(curl -sL "${mihomo_link}/latest/download/version.txt") 134 | fi 135 | 136 | mihomo_version=$(curl -sL "${mihomo_link}/download/${mihomo_tag}/version.txt") 137 | if [[ `echo "${mihomo_version}" | grep "Not Found"` ]]; then 138 | echo "mihomo链接不可用" 139 | echo "测试链接 ${mihomo_link}/${mihomo_tag}/version.txt" 140 | exit 1 141 | else 142 | echo "mihomo版本:${mihomo_version}" 143 | fi 144 | 145 | } 146 | 147 | ci_pack(){ 148 | pack 149 | filename="MFM-${pack_arch}-`cat ./version | awk -F ':' '{print $2}'`.zip" 150 | mkdir -p release-$pack_arch 151 | echo "CI:解压中" 152 | unzip ./release/$filename -d release-$pack_arch 153 | } 154 | 155 | while getopts "abcdgpt" opt; do 156 | case $opt in 157 | a) 158 | download_binaries 159 | download_geoX 160 | download_config 161 | download_dashboard 162 | pack 163 | ;; 164 | b) 165 | download_binaries 166 | ;; 167 | 168 | c) # for CI usage 169 | download_binaries 170 | download_geoX 171 | download_config 172 | download_dashboard 173 | ci_pack 174 | ;; 175 | d) 176 | download_dashboard 177 | ;; 178 | g) 179 | download_geoX 180 | ;; 181 | c) 182 | download_config 183 | ;; 184 | p) 185 | pack 186 | ;; 187 | t) 188 | test_pack 189 | ;; 190 | \?) 191 | echo "Invalid option: -$OPTARG" >&2 192 | ;; 193 | esac 194 | done -------------------------------------------------------------------------------- /clash/scripts/clash.service: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | 3 | scripts=`realpath $0` 4 | scripts_dir=`dirname ${scripts}` 5 | . /data/clash/clash.config 6 | 7 | get_uid(){ 8 | uid_proxy="payload:" 9 | for app in $1 ; do 10 | app_uid=$(dumpsys package $app | grep "userId" | tail -n -1 | cut -c 12-) 11 | if [ -n "$app_uid" ] ; then 12 | uid_proxy+="\n - UID,"${app_uid} 13 | fi 14 | done 15 | echo $uid_proxy > $2 16 | } 17 | 18 | clean_last(){ 19 | if [ -d ${Clash_run_path} ] ; then 20 | rm -rf ${Clash_run_path}.old 21 | mv -f ${Clash_run_path} ${Clash_run_path}.old 22 | fi 23 | mkdir -p ${Clash_run_path} 24 | chown ${Clash_user_group} ${Clash_run_path} 25 | chmod 0755 ${Clash_run_path} 26 | 27 | touch $CFM_logs_file 28 | touch $appuid_file 29 | touch $Clash_pid_file 30 | 31 | } 32 | 33 | start_clash() { 34 | clean_last 35 | if [ -n "$app_proxy" ] ; then 36 | get_uid "$app_proxy" "${Clash_data_dir}/rule_providers/${proxy_file}" 37 | fi 38 | if [ -n "$app_direct" ] ; then 39 | get_uid "$app_direct" "${Clash_data_dir}/rule_providers/${direct_file}" 40 | fi 41 | if [ "${ipv6}" = "false" ] ; then 42 | for net in /proc/sys/net/ipv6/conf/{wlan*,*data*} ; do 43 | # AVD中可能不存在data 44 | if [ -d ${net} ] ; then 45 | echo ${net} | grep -q wlan 46 | if [ $? -eq 0 ] ; then 47 | echo 0 > ${net}/accept_ra 48 | fi 49 | echo 1 > ${net}/disable_ipv6 50 | fi 51 | done 52 | else 53 | for net in /proc/sys/net/ipv6/conf/{wlan*,*data*} ; do 54 | if [ -d ${net} ] ; then 55 | echo ${net} | grep -q wlan 56 | if [ $? -eq 0 ] ; then 57 | echo 1 > ${net}/accept_ra 58 | fi 59 | echo 0 > ${net}/disable_ipv6 60 | fi 61 | done 62 | fi 63 | echo "" > ${CFM_logs_file} 64 | pid=`cat ${Clash_pid_file} 2> /dev/null` 65 | if (cat /proc/${pid}/cmdline | grep -q clash) ; then 66 | echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"info: 检测到clash已启动,此次不启动." >> ${CFM_logs_file} 67 | exit 1 68 | fi 69 | 70 | if [ "${Clash_tproxy_port}" == 0 ] || [ "${Clash_tproxy_port}" == "" ]; then 71 | if [ "${Clash_tun_status}" != "true" ]; then 72 | echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"err: tproxy和tun得二选一." >> ${CFM_logs_file} 73 | exit 1 74 | fi 75 | fi 76 | 77 | # 合并文件逻辑 78 | if ! grep -q "^dns:$" ${template_file} ; then 79 | awk '/dns:/ {exit} {print $0}' ${template_file} >> ${temporary_config_file}.swp 80 | echo "\n" >> ${temporary_config_file}.swp 81 | sed -n -E '/^dns:.*$/,$p' ${Clash_config_file} >> ${temporary_config_file}.swp 82 | else # 从 proxies 开始 83 | cp -f ${template_file} ${temporary_config_file}.swp && echo "\n" >> ${temporary_config_file}.swp 84 | sed -n -E '/^proxies:.*$/,$p' ${Clash_config_file} >> ${temporary_config_file}.swp 85 | fi 86 | 87 | sed -i '/^[ ]*$/d' ${temporary_config_file}.swp 88 | 89 | mv -f ${temporary_config_file}.swp ${temporary_config_file} \ 90 | && echo "" >> ${CFM_logs_file} || echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"err:配置文件合并失败" 91 | 92 | if [ -f "${Clash_bin_path}" ] ; then 93 | mkdir -p ${Clash_run_path} 94 | chown ${Clash_user_group} ${Clash_bin_path} 95 | chmod ${Clash_permissions} ${Clash_bin_path} 96 | chown ${Clash_user_group} ${temporary_config_file} 97 | chmod 0644 ${temporary_config_file} 98 | 99 | ${busybox_path} crontab -c ${Clash_run_path} -r 100 | touch ${Clash_run_path}/root 101 | chmod 0600 ${Clash_run_path}/root 102 | 103 | if [ "${auto_updateGeoSite}" == "true" ]; then 104 | echo "${update_geoXInterval} ${scripts_dir}/clash.tool -u" >> ${Clash_run_path}/root \ 105 | && echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"info: 自动更新GeoX定时已开启." | tee -a ${CFM_logs_file} 106 | fi 107 | 108 | if [ "${auto_updateSubcript}" == "true" ]; then 109 | if [ -z "$Subcript_url" ]; then 110 | echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"warn: 自动更新订阅定时已开启,但未设置订阅链接" | tee -a ${CFM_logs_file} 111 | else 112 | echo "${update_subcriptInterval} ${scripts_dir}/clash.tool -s" >> ${Clash_run_path}/root \ 113 | && echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"info: 自动更新订阅定时已开启." | tee -a ${CFM_logs_file} 114 | fi 115 | fi 116 | 117 | sed -i '/^[ ]*$/d' ${CFM_logs_file} 118 | if [ "${filter_local}" = "true" ] ; then 119 | com="${scripts_dir}/clash.tool -m ; sleep 10 ;${scripts_dir}/clash.tool -m ; sleep 10; ${scripts_dir}/clash.tool -m ;sleep 10; ${scripts_dir}/clash.tool -m;sleep 10;${scripts_dir}/clash.tool -m ; sleep 10;${scripts_dir}/clash.tool -m" 120 | echo "*/1 * * * * ${com}" >> ${Clash_run_path}/root && echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"info: 自动绕过本地ip段已打开." >> ${CFM_logs_file} 121 | fi 122 | else 123 | echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"err: clash内核丢失." >> ${CFM_logs_file} 124 | exit 1 125 | fi 126 | 127 | if [ -f ${temporary_config_file} ] && [ -f ${Clash_GeoIP_file} ] ; then 128 | if $(${Clash_bin_path} -d ${Clash_data_dir} -t -f ${temporary_config_file} > /dev/null) ; then 129 | if [ "${Clash_tun_status}" == "true" ]; then 130 | mkdir -p /dev/net 131 | ln -sf /dev/tun /dev/net/tun 132 | fi 133 | [ ! -f /data/clash/run/kernel.log ] || mv /data/clash/run/kernel.log -f /data/clash/run/kernel.old.log 134 | ulimit -SHn 1000000 135 | nohup ${busybox_path} setuidgid ${Clash_user_group} ${Clash_bin_path} -d ${Clash_data_dir} -f ${temporary_config_file} > /data/clash/run/kernel.log 2>&1 & 136 | echo -n $! > ${Clash_pid_file} 137 | ${scripts_dir}/clash.tool -l 138 | echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"info: clash内核已启动." >> ${CFM_logs_file} 139 | else 140 | echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"info: Clash启动失败 配置错误 错误信息" >> ${CFM_logs_file} 141 | $(${Clash_bin_path} -d ${Clash_data_dir} -f ${Clash_config_file} -t | grep error| awk -F 'msg=' '{print $2}'>> ${CFM_logs_file}) 142 | kill_clash 143 | exit 1 144 | fi 145 | else 146 | echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"info: 文件缺失." >> ${CFM_logs_file} 147 | exit 1 148 | fi 149 | 150 | ${scripts_dir}/clash.iptables -s 151 | return # 去掉端口检查 152 | 153 | if [ "${Clash_tun_status}" == "true" ]; then 154 | return 155 | fi 156 | sleep 5 157 | if ! (${scripts_dir}/clash.tool -p) ; then 158 | kill_clash 159 | rm -rf ${Clash_pid_file} 160 | echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"err: 端口未检测到,已停止clash内核以及后续逻辑." >> ${CFM_logs_file} 161 | exit 1 162 | fi 163 | } 164 | 165 | stop_clash() { 166 | ${scripts_dir}/clash.iptables -k 167 | kill_clash 168 | echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"info: 停止clash内核.\n" | tee -a ${CFM_logs_file} 169 | } 170 | 171 | kill_clash() { 172 | pids=`pgrep -f clash` 173 | for pid in $pids ; do 174 | kill -s SIGTERM $pid 175 | done 176 | } 177 | 178 | 179 | while getopts ":skc" signal ; do 180 | case ${signal} in 181 | c) 182 | clean_last 183 | ;; 184 | s) 185 | start_clash 186 | ;; 187 | k) 188 | stop_clash 189 | ;; 190 | ?) 191 | echo "" 192 | ;; 193 | esac 194 | done 195 | -------------------------------------------------------------------------------- /clash/scripts/clash.tool: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | 3 | scripts=$(realpath $0) 4 | scripts_dir=$(dirname ${scripts}) 5 | . /data/clash/clash.config 6 | 7 | monitor_local_ipv4() { 8 | 9 | change=false 10 | 11 | wifistatus=$(dumpsys connectivity | grep "WIFI" | grep "state:" | awk -F ", " '{print $2}' | awk -F "=" '{print $2}' 2>&1) 12 | 13 | if [ ! -z "${wifistatus}" ]; then 14 | if test ! "${wifistatus}" = "$(cat ${Clash_run_path}/lastwifi)"; then 15 | change=true 16 | echo "${wifistatus}" >${Clash_run_path}/lastwifi 17 | elif [ "$(ip route get 1.2.3.4 | awk '{print $5}' 2>&1)" != "wlan0" ]; then 18 | change=true 19 | echo "${wifistatus}" >${Clash_run_path}/lastwifi 20 | fi 21 | else 22 | echo "" >${Clash_run_path}/lastwifi 23 | fi 24 | 25 | if [ "$(settings get global mobile_data 2>&1)" -eq 1 ] || [ "$(settings get global mobile_data1 2>&1)" -eq 1 ]; then 26 | if [ ! "${mobilestatus}" = "$(cat ${Clash_run_path}/lastmobile)" ]; then 27 | change=true 28 | echo "${mobilestatus}" >${Clash_run_path}/lastmobile 29 | fi 30 | fi 31 | 32 | if [ ${change} == true ]; then 33 | 34 | local_ipv4=$(ip a | awk '$1~/inet$/{print $2}') 35 | local_ipv6=$(ip -6 a | awk '$1~/inet6$/{print $2}') 36 | rules_ipv4=$(${iptables_wait} -t mangle -nvL FILTER_LOCAL_IP | grep "ACCEPT" | awk '{print $9}' 2>&1) 37 | rules_ipv6=$(${ip6tables_wait} -t mangle -nvL FILTER_LOCAL_IP | grep "ACCEPT" | awk '{print $8}' 2>&1) 38 | 39 | for rules_subnet in ${rules_ipv4[*]}; do 40 | ${iptables_wait} -t mangle -D FILTER_LOCAL_IP -d ${rules_subnet} -j ACCEPT 41 | done 42 | 43 | for subnet in ${local_ipv4[*]}; do 44 | if ! (${iptables_wait} -t mangle -C FILTER_LOCAL_IP -d ${subnet} -j ACCEPT >/dev/null 2>&1); then 45 | ${iptables_wait} -t mangle -I FILTER_LOCAL_IP -d ${subnet} -j ACCEPT 46 | fi 47 | done 48 | 49 | for rules_subnet6 in ${rules_ipv6[*]}; do 50 | ${ip6tables_wait} -t mangle -D FILTER_LOCAL_IP -d ${rules_subnet6} -j ACCEPT 51 | done 52 | 53 | for subnet6 in ${local_ipv6[*]}; do 54 | if ! (${ip6tables_wait} -t mangle -C FILTER_LOCAL_IP -d ${subnet6} -j ACCEPT >/dev/null 2>&1); then 55 | ${ip6tables_wait} -t mangle -I FILTER_LOCAL_IP -d ${subnet6} -j ACCEPT 56 | fi 57 | done 58 | fi 59 | 60 | unset local_ipv4 61 | unset rules_ipv4 62 | unset local_ipv6 63 | unset rules_ipv6 64 | unset wifistatus 65 | unset mobilestatus 66 | unset change 67 | } 68 | 69 | restart_clash() { 70 | ${scripts_dir}/clash.service -k && ${scripts_dir}/clash.iptables -k 71 | sleep 5 72 | ${scripts_dir}/clash.service -s && ${scripts_dir}/clash.iptables -s 73 | if [ "$?" == "0" ]; then 74 | echo [$(TZ=Asia/Shanghai date "+%H:%M:%S")]"info: 内核成功重启." >>${CFM_logs_file} 75 | else 76 | echo [$(TZ=Asia/Shanghai date "+%H:%M:%S")]"err: 内核重启失败." >>${CFM_logs_file} 77 | fi 78 | } 79 | 80 | keep_dns() { 81 | local_dns=$(getprop net.dns1) 82 | 83 | if [ "${local_dns}" != "${static_dns}" ]; then 84 | for count in $(seq 1 $(getprop | grep dns | wc -l)); do 85 | setprop net.dns${count} ${static_dns} 86 | done 87 | fi 88 | 89 | if [ $(sysctl net.ipv4.ip_forward) != "1" ]; then 90 | sysctl -w net.ipv4.ip_forward=1 91 | fi 92 | 93 | unset local_dns 94 | } 95 | 96 | updateFile() { 97 | file="$1" 98 | file_bk="${file}.bk" 99 | update_url="$2" 100 | 101 | mv -f ${file} ${file_bk} 102 | echo "curl --cacert /etc/security/cacerts/cacert.pem -L -A 'clash' ${update_url} -o ${file} " 103 | curl --cacert /etc/security/cacerts/cacert.pem -L -A 'clash' ${update_url} -o ${file} 2>&1 # >> /dev/null 2>&1 104 | 105 | sleep 1 106 | 107 | if [ -f "${file}" ]; then 108 | rm -rf ${file_bk} 109 | 110 | echo [$(TZ=Asia/Shanghai date "+%H:%M:%S")]"info: ${file}更新成功." >>${CFM_logs_file} 111 | else 112 | mv ${file_bk} ${file} 113 | echo [$(TZ=Asia/Shanghai date "+%H:%M:%S")]"war: ${file}更新失败,文件已恢复.." >>${CFM_logs_file} 114 | return 1 115 | fi 116 | } 117 | 118 | find_packages_uid() { 119 | rm -f ${appuid_file}.tmp 120 | hd="" 121 | for package in $(cat ${filter_packages_file}); do 122 | if [ "${Clash_enhanced_mode}" == "fake-ip" ] && [ "${Clash_tun_status}" != "true" ]; then 123 | echo [$(TZ=Asia/Shanghai date "+%H:%M:%S")]"war: Tproxy_fake-ip下禁用黑白名单." >>${CFM_logs_file} 124 | return 125 | fi 126 | nhd=$(awk -F ">" '/^[0-9]+>$/{print $1}' <<< "${package}") 127 | if [ "${nhd}" != "" ]; then 128 | hd=${nhd} 129 | continue 130 | fi 131 | uid=$(awk '$1~/'^"${package}"$'/{print $2}' ${system_packages_file}) 132 | if [ "${uid}" == "" ]; then 133 | echo [$(TZ=Asia/Shanghai date "+%H:%M:%S")]"warn: ${package}未找到." >>${CFM_logs_file} 134 | continue 135 | fi 136 | echo "${hd}${uid}" >> ${appuid_file}.tmp 137 | if [ "${mode}" = "blacklist" ]; then 138 | echo [$(TZ=Asia/Shanghai date "+%H:%M:%S")]"info: ${hd}${package}已过滤." >>${CFM_logs_file} 139 | elif [ "${mode}" = "whitelist" ]; then 140 | echo [$(TZ=Asia/Shanghai date "+%H:%M:%S")]"info: ${hd}${package}已代理." >>${CFM_logs_file} 141 | fi 142 | done 143 | rm -f ${appuid_file} 144 | for uid in $(cat ${appuid_file}.tmp | sort -u); do 145 | echo ${uid} >> ${appuid_file} 146 | done 147 | rm -f ${appuid_file}.tmp 148 | } 149 | 150 | port_detection() { 151 | clash_pid=$(cat ${Clash_pid_file}) 152 | match_count=0 153 | 154 | if ! (ss -h >/dev/null 2>&1); then 155 | clash_port=$(netstat -anlp | grep -v p6 | grep "clash" | awk '$6~/'"${clash_pid}"*'/{print $4}' | awk -F ':' '{print $2}' | sort -u) 156 | else 157 | clash_port=$(ss -antup | grep "clash" | awk '$7~/'pid="${clash_pid}"*'/{print $5}' | awk -F ':' '{print $2}' | sort -u) 158 | fi 159 | 160 | if ! (echo ${clash_port} | grep ${Clash_tproxy_port}); then 161 | echo [$(TZ=Asia/Shanghai date "+%H:%M:%S")]"err: tproxy端口未启动." >>${CFM_logs_file} 162 | exit 1 163 | fi 164 | 165 | if ! (echo ${clash_port} | grep ${Clash_dns_port}); then 166 | echo [$(TZ=Asia/Shanghai date "+%H:%M:%S")]"err: dns端口未启动." >>${CFM_logs_file} 167 | exit 1 168 | fi 169 | echo [$(TZ=Asia/Shanghai date "+%H:%M:%S")]"info: tproxy和dns端口已启动." >>${CFM_logs_file} 170 | exit 0 171 | } 172 | 173 | update_pre() { 174 | flag=false 175 | 176 | if [ ${auto_updateGeoIP} == "true" ]; then 177 | updateFile ${Clash_GeoIP_file} ${GeoIP_url} 178 | if [ "$?" = "0" ]; then 179 | flag=true 180 | fi 181 | fi 182 | 183 | if [ ${auto_updateGeoSite} == "true" ]; then 184 | updateFile ${Clash_GeoSite_file} ${GeoSite_url} 185 | if [ "$?" = "0" ]; then 186 | flag=true 187 | fi 188 | fi 189 | 190 | if [ -f "${Clash_pid_file}" ] && [ ${flag} == true ]; then 191 | restart_clash 192 | fi 193 | } 194 | 195 | limit_clash() { 196 | if [ "${Cgroup_memory_limit}" == "" ]; then 197 | return 198 | fi 199 | 200 | if [ "${Cgroup_memory_path}" == "" ]; then 201 | Cgroup_memory_path=$(mount | grep cgroup | awk '/memory/{print $3}' | head -1) 202 | if [ "${Cgroup_memory_path}" == "" ]; then 203 | echo [$(TZ=Asia/Shanghai date "+%H:%M:%S")]"err: 自动获取Cgroup_memory_path失败." >>${CFM_logs_file} 204 | return 205 | fi 206 | fi 207 | 208 | mkdir "${Cgroup_memory_path}/clash" 209 | echo $(cat ${Clash_pid_file}) >"${Cgroup_memory_path}/clash/cgroup.procs" 210 | echo "${Cgroup_memory_limit}" >"${Cgroup_memory_path}/clash/memory.limit_in_bytes" 211 | 212 | echo [$(TZ=Asia/Shanghai date "+%H:%M:%S")]"info: 限制内存: ${Cgroup_memory_limit}." >>${CFM_logs_file} 213 | } 214 | 215 | update_cofig() { 216 | # Updat config.yaml 217 | # 1. generate new `config.yaml` and run directory 218 | . $(dirname $0)/clash.service 219 | start_clash 220 | # 2. restart with api 221 | # See https://wiki.metacubex.one/api/ 222 | controller_api=$(grep 'external-controller:' $(dirname $0)/../template | cut -d' ' -f2) 223 | secret=$(grep 'secret:' $(dirname $0)/../template | cut -d' ' -f2) 224 | if [ -z ${secret}]; then 225 | curl -s -X PUT 'http://'${controller_api}'/configs?force=true' -d '{"path": "", "payload": ""}' 226 | else 227 | curl -s -X PUT -H 'Authorization: Bearer '${secret}'' 'http://'${controller_api}'/configs?force=true' -d '{"path": "", "payload": ""}' 228 | fi 229 | echo [$(TZ=Asia/Shanghai date "+%H:%M:%S")]"info: 订阅更新成功." >>${CFM_logs_file} 230 | } 231 | 232 | while getopts ":kfmpusl" signal; do 233 | case ${signal} in 234 | u) 235 | update_pre 236 | ;; 237 | s) 238 | . $(dirname $0)/updateSub.sh 239 | updateSub 240 | update_cofig 241 | ;; 242 | k) 243 | if [ "${mode}" = "blacklist" ] || [ "${mode}" = "whitelist" ]; then 244 | keep_dns 245 | else 246 | exit 0 247 | fi 248 | ;; 249 | f) 250 | find_packages_uid 251 | ;; 252 | m) 253 | if [ "${mode}" = "blacklist" ] && [ -f "${Clash_pid_file}" ]; then 254 | monitor_local_ipv4 255 | else 256 | exit 0 257 | fi 258 | ;; 259 | p) 260 | port_detection 261 | ;; 262 | l) 263 | limit_clash 264 | ;; 265 | ?) 266 | echo "" 267 | ;; 268 | esac 269 | done 270 | -------------------------------------------------------------------------------- /clash/scripts/clash.iptables: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | 3 | scripts=`realpath $0` 4 | scripts_dir=`dirname ${scripts}` 5 | . /data/clash/clash.config 6 | 7 | create_rule_rules() { 8 | ip -4 rule add fwmark ${mark_id} table ${table_id} pref ${pref_id} 9 | ip -4 route add local default dev lo table ${table_id} 10 | if [ "${ipv6}" = "true" ] ; then 11 | ip -6 rule add fwmark ${mark_id} table ${table_id} pref ${pref_id} 12 | ip -6 route add local default dev lo table ${table_id} 13 | fi 14 | } 15 | 16 | flush_rule_rules() { 17 | ip rule del fwmark ${mark_id} table ${table_id} 18 | ip route flush table ${table_id} 19 | if [ "${ipv6}" = "true" ] ; then 20 | ip -6 rule del fwmark ${mark_id} table ${table_id} 21 | ip -6 route flush table ${table_id} 22 | fi 23 | } 24 | 25 | create_mangle_prerouting_chain() { 26 | ${iptables_wait} -t mangle -N CLASH_PRE 27 | 28 | ${iptables_wait} -t mangle -A CLASH_PRE -p tcp -j TPROXY --on-port ${Clash_tproxy_port} --tproxy-mark ${mark_id} && echo "" || echo "你的系统可能阉割了Tproxy-TCP" >> ${CFM_logs_file} 29 | ${iptables_wait} -t mangle -A CLASH_PRE -p udp ! --dport 53 -j TPROXY --on-port ${Clash_tproxy_port} --tproxy-mark ${mark_id} && echo "" || echo "你的系统可能阉割了Tproxy-UDP" >> ${CFM_logs_file} 30 | if [ "${ipv6}" = "true" ] ; then 31 | ${ip6tables_wait} -t mangle -N CLASH_PRE 32 | 33 | ${ip6tables_wait} -t mangle -A CLASH_PRE -p tcp -j TPROXY --on-port ${Clash_tproxy_port} --tproxy-mark ${mark_id} && echo "" || echo "你的系统可能阉割了Tproxy-TCP6" >> ${CFM_logs_file} 34 | ${ip6tables_wait} -t mangle -A CLASH_PRE -p udp ! --dport 53 -j TPROXY --on-port ${Clash_tproxy_port} --tproxy-mark ${mark_id} && echo "" || echo "你的系统可能阉割了Tproxy-UDP6" >> ${CFM_logs_file} 35 | fi 36 | } 37 | 38 | create_mangle_output_chain() { 39 | ${iptables_wait} -t mangle -N CLASH_OUT 40 | 41 | ${iptables_wait} -t mangle -A CLASH_OUT -p tcp -j MARK --set-mark ${mark_id} 42 | ${iptables_wait} -t mangle -A CLASH_OUT -p udp -j MARK --set-mark ${mark_id} 43 | if [ "${ipv6}" = "true" ] ; then 44 | ${ip6tables_wait} -t mangle -N CLASH_OUT 45 | 46 | ${ip6tables_wait} -t mangle -A CLASH_OUT -p tcp -j MARK --set-mark ${mark_id} 47 | ${ip6tables_wait} -t mangle -A CLASH_OUT -p udp -j MARK --set-mark ${mark_id} 48 | fi 49 | } 50 | 51 | create_mangle_prerouting_filter() { 52 | ${iptables_wait} -t mangle -N FILTER_PRE_CLASH 53 | 54 | for subnet in ${reserved_ip[*]} ; do 55 | ${iptables_wait} -t mangle -A FILTER_PRE_CLASH -d ${subnet} -j ACCEPT 56 | done 57 | 58 | ${iptables_wait} -t mangle -A FILTER_PRE_CLASH -j CLASH_PRE 59 | if [ "${ipv6}" = "true" ] ; then 60 | ${ip6tables_wait} -t mangle -N FILTER_PRE_CLASH 61 | 62 | for subnet6 in ${reserved_ip6[*]} ; do 63 | ${ip6tables_wait} -t mangle -A FILTER_PRE_CLASH -d ${subnet6} -j ACCEPT 64 | done 65 | 66 | ${ip6tables_wait} -t mangle -A FILTER_PRE_CLASH -j CLASH_PRE 67 | fi 68 | } 69 | 70 | create_mangle_output_filter() { 71 | ${scripts_dir}/clash.tool -f 72 | 73 | ${iptables_wait} -t mangle -N FILTER_OUT_CLASH 74 | 75 | for subnet in ${reserved_ip[*]} ; do 76 | ${iptables_wait} -t mangle -A FILTER_OUT_CLASH -d ${subnet} -j ACCEPT 77 | done 78 | 79 | if [ "${Clash_enhanced_mode}" == "fake-ip" ]; then 80 | mode="blacklist" 81 | fi 82 | apps=`cat ${appuid_file} | sort -u` 83 | if [ "${mode}" = "blacklist" ] ; then 84 | for appuid in ${apps} ; do 85 | ${iptables_wait} -t mangle -A FILTER_OUT_CLASH -m owner --uid-owner ${appuid} -j ACCEPT 86 | done 87 | 88 | ${iptables_wait} -t mangle -A FILTER_OUT_CLASH -m owner ! --gid-owner ${Clash_group} -j CLASH_OUT 89 | elif [ "${mode}" = "whitelist" ] ; then 90 | for appuid in ${apps} ; do 91 | ${iptables_wait} -t mangle -A FILTER_OUT_CLASH -m owner --uid-owner ${appuid} -j CLASH_OUT 92 | done 93 | fi 94 | 95 | if [ "${ipv6}" = "true" ] ; then 96 | ${ip6tables_wait} -t mangle -N FILTER_OUT_CLASH 97 | 98 | for subnet6 in ${reserved_ip6[*]} ; do 99 | ${ip6tables_wait} -t mangle -A FILTER_OUT_CLASH -d ${subnet6} -j ACCEPT 100 | done 101 | 102 | if [ "${mode}" = "blacklist" ] ; then 103 | for appuid in ${apps} ; do 104 | ${ip6tables_wait} -t mangle -A FILTER_OUT_CLASH -m owner --uid-owner ${appuid} -j ACCEPT 105 | done 106 | 107 | ${ip6tables_wait} -t mangle -A FILTER_OUT_CLASH -m owner ! --gid-owner ${Clash_group} -j CLASH_OUT 108 | elif [ "${mode}" = "whitelist" ] ; then 109 | for appuid in ${apps} ; do 110 | ${ip6tables_wait} -t mangle -A FILTER_OUT_CLASH -m owner --uid-owner ${appuid} -j CLASH_OUT 111 | done 112 | fi 113 | fi 114 | } 115 | 116 | create_nat_prerouting_dns() { 117 | ${iptables_wait} -t nat -N DNS_PRE 118 | 119 | ${iptables_wait} -t nat -A DNS_PRE -p udp -j REDIRECT --to-ports ${Clash_dns_port} 120 | if [ "${ipv6}" = "true" ] ; then 121 | if [ "${nat_kernel}" = "true" ] ; then 122 | ${ip6tables_wait} -t nat -N DNS_PRE 123 | 124 | ${ip6tables_wait} -t nat -A DNS_PRE -p udp -j REDIRECT --to-ports ${Clash_dns_port} 125 | fi 126 | fi 127 | } 128 | 129 | create_nat_output_dns() { 130 | ${iptables_wait} -t nat -N DNS_OUT 131 | 132 | ${iptables_wait} -t nat -A DNS_OUT -p udp -j REDIRECT --to-ports ${Clash_dns_port} 133 | if [ "${ipv6}" = "true" ] ; then 134 | if [ "${nat_kernel}" = "true" ] ; then 135 | ${ip6tables_wait} -t nat -N DNS_OUT 136 | 137 | ${ip6tables_wait} -t nat -A DNS_OUT -p udp -j REDIRECT --to-ports ${Clash_dns_port} 138 | fi 139 | fi 140 | } 141 | 142 | create_nat_output_filter() { 143 | ${iptables_wait} -t nat -N FILTER_OUT_DNS 144 | 145 | ${iptables_wait} -t nat -A FILTER_OUT_DNS -m owner --gid-owner ${Clash_group} -j ACCEPT 146 | ${iptables_wait} -t nat -A FILTER_OUT_DNS -p udp --dport 53 -j DNS_OUT 147 | if [ "${ipv6}" = "true" ] ; then 148 | if [ "${nat_kernel}" = "true" ] ; then 149 | ${ip6tables_wait} -t nat -N FILTER_OUT_DNS 150 | 151 | ${ip6tables_wait} -t nat -A FILTER_OUT_DNS -m owner --gid-owner ${Clash_group} -j ACCEPT 152 | ${ip6tables_wait} -t nat -A FILTER_OUT_DNS -p udp --dport 53 -j DNS_OUT 153 | fi 154 | fi 155 | } 156 | 157 | create_nat_prerouting_filter() { 158 | ${iptables_wait} -t nat -N FILTER_PRE_DNS 159 | 160 | ${iptables_wait} -t nat -A FILTER_PRE_DNS -p udp --dport 53 -j DNS_PRE 161 | if [ "${ipv6}" = "true" ] ; then 162 | if [ "${nat_kernel}" = "true" ] ; then 163 | ${ip6tables_wait} -t nat -N FILTER_PRE_DNS 164 | 165 | ${ip6tables_wait} -t nat -A FILTER_PRE_DNS -p udp --dport 53 -j DNS_PRE 166 | fi 167 | fi 168 | } 169 | 170 | creat_pre_divert() { 171 | ${iptables_wait} -t mangle -N DIVERT 172 | 173 | ${iptables_wait} -t mangle -A DIVERT -j MARK --set-mark ${mark_id} 174 | 175 | ${iptables_wait} -t mangle -A DIVERT -j ACCEPT 176 | if [ "${ipv6}" = "true" ] ; then 177 | ${ip6tables_wait} -t mangle -N DIVERT 178 | 179 | ${ip6tables_wait} -t mangle -A DIVERT -j MARK --set-mark ${mark_id} 180 | 181 | ${ip6tables_wait} -t mangle -A DIVERT -j ACCEPT 182 | fi 183 | } 184 | 185 | apply_rules() { 186 | create_mangle_prerouting_chain 187 | create_mangle_prerouting_filter 188 | 189 | create_mangle_output_chain 190 | create_mangle_output_filter 191 | 192 | create_nat_prerouting_dns 193 | create_nat_prerouting_filter 194 | 195 | create_nat_output_dns 196 | create_nat_output_filter 197 | 198 | creat_pre_divert 199 | ${iptables_wait} -t mangle -A PREROUTING -p tcp -m socket -j DIVERT 200 | ${iptables_wait} -t mangle -A PREROUTING -p udp -m socket -j DIVERT 201 | 202 | ${iptables_wait} -t mangle -N FILTER_LOCAL_IP 203 | ${iptables_wait} -t mangle -A PREROUTING -j FILTER_LOCAL_IP 204 | ${iptables_wait} -t mangle -A OUTPUT -j FILTER_LOCAL_IP 205 | 206 | ${iptables_wait} -t mangle -A PREROUTING -j FILTER_PRE_CLASH 207 | ${iptables_wait} -t mangle -A OUTPUT -j FILTER_OUT_CLASH 208 | 209 | ${iptables_wait} -t nat -A PREROUTING -j FILTER_PRE_DNS 210 | ${iptables_wait} -t nat -A OUTPUT -j FILTER_OUT_DNS 211 | 212 | if [ "${ipv6}" = "true" ] ; then 213 | ${ip6tables_wait} -t mangle -A PREROUTING -p tcp -m socket -j DIVERT 214 | ${ip6tables_wait} -t mangle -A PREROUTING -p udp -m socket -j DIVERT 215 | 216 | ${ip6tables_wait} -t mangle -N FILTER_LOCAL_IP 217 | ${ip6tables_wait} -t mangle -A PREROUTING -j FILTER_LOCAL_IP 218 | ${ip6tables_wait} -t mangle -A OUTPUT -j FILTER_LOCAL_IP 219 | 220 | ${ip6tables_wait} -t mangle -A PREROUTING -j FILTER_PRE_CLASH 221 | ${ip6tables_wait} -t mangle -A OUTPUT -j FILTER_OUT_CLASH 222 | 223 | if [ "${nat_kernel}" = "true" ] ; then 224 | ${ip6tables_wait} -t nat -A PREROUTING -j FILTER_PRE_DNS 225 | ${ip6tables_wait} -t nat -A OUTPUT -j FILTER_OUT_DNS 226 | else 227 | ${ip6tables_wait} -I OUTPUT -p udp --dport 53 -j DROP 228 | fi 229 | fi 230 | 231 | ${scripts_dir}/clash.tool -m 232 | } 233 | 234 | flush_rules() { 235 | ${iptables_wait} -t nat -D OUTPUT -j FILTER_OUT_DNS 236 | ${iptables_wait} -t nat -D PREROUTING -j FILTER_PRE_DNS 237 | 238 | ${iptables_wait} -t mangle -D OUTPUT -j FILTER_OUT_CLASH 239 | ${iptables_wait} -t mangle -D PREROUTING -j FILTER_PRE_CLASH 240 | 241 | ${iptables_wait} -t mangle -D OUTPUT -j FILTER_LOCAL_IP 242 | ${iptables_wait} -t mangle -D PREROUTING -j FILTER_LOCAL_IP 243 | ${iptables_wait} -t mangle -F FILTER_LOCAL_IP 244 | ${iptables_wait} -t mangle -X FILTER_LOCAL_IP 245 | 246 | ${iptables_wait} -t mangle -D PREROUTING -p tcp -m socket -j DIVERT 247 | ${iptables_wait} -t mangle -D PREROUTING -p udp -m socket -j DIVERT 248 | ${iptables_wait} -t mangle -F DIVERT 249 | ${iptables_wait} -t mangle -X DIVERT 250 | 251 | ${iptables_wait} -t nat -F FILTER_OUT_DNS 252 | ${iptables_wait} -t nat -X FILTER_OUT_DNS 253 | ${iptables_wait} -t nat -F DNS_OUT 254 | ${iptables_wait} -t nat -X DNS_OUT 255 | 256 | ${iptables_wait} -t nat -F FILTER_PRE_DNS 257 | ${iptables_wait} -t nat -X FILTER_PRE_DNS 258 | ${iptables_wait} -t nat -F DNS_PRE 259 | ${iptables_wait} -t nat -X DNS_PRE 260 | 261 | ${iptables_wait} -t mangle -F FILTER_OUT_CLASH 262 | ${iptables_wait} -t mangle -X FILTER_OUT_CLASH 263 | ${iptables_wait} -t mangle -F CLASH_OUT 264 | ${iptables_wait} -t mangle -X CLASH_OUT 265 | 266 | ${iptables_wait} -t mangle -F FILTER_PRE_CLASH 267 | ${iptables_wait} -t mangle -X FILTER_PRE_CLASH 268 | ${iptables_wait} -t mangle -F CLASH_PRE 269 | ${iptables_wait} -t mangle -X CLASH_PRE 270 | 271 | if [ "${ipv6}" = "true" ] ; then 272 | 273 | if [ "${nat_kernel}" = "true" ] ; then 274 | ${ip6tables_wait} -t nat -D OUTPUT -j FILTER_OUT_DNS 275 | ${ip6tables_wait} -t nat -D PREROUTING -j FILTER_PRE_DNS 276 | ${ip6tables_wait} -t nat -F FILTER_OUT_DNS 277 | ${ip6tables_wait} -t nat -X FILTER_OUT_DNS 278 | ${ip6tables_wait} -t nat -F DNS_OUT 279 | ${ip6tables_wait} -t nat -X DNS_OUT 280 | ${ip6tables_wait} -t nat -F FILTER_PRE_DNS 281 | ${ip6tables_wait} -t nat -X FILTER_PRE_DNS 282 | ${ip6tables_wait} -t nat -F DNS_PRE 283 | ${ip6tables_wait} -t nat -X DNS_PRE 284 | else 285 | ${ip6tables_wait} -D OUTPUT -p udp --dport 53 -j DROP 286 | fi 287 | 288 | ${ip6tables_wait} -t mangle -D OUTPUT -j FILTER_OUT_CLASH 289 | ${ip6tables_wait} -t mangle -D PREROUTING -j FILTER_PRE_CLASH 290 | 291 | ${ip6tables_wait} -t mangle -D OUTPUT -j FILTER_LOCAL_IP 292 | ${ip6tables_wait} -t mangle -D PREROUTING -j FILTER_LOCAL_IP 293 | ${ip6tables_wait} -t mangle -F FILTER_LOCAL_IP 294 | ${ip6tables_wait} -t mangle -X FILTER_LOCAL_IP 295 | 296 | ${ip6tables_wait} -t mangle -D PREROUTING -p tcp -m socket -j DIVERT 297 | ${ip6tables_wait} -t mangle -D PREROUTING -p udp -m socket -j DIVERT 298 | ${ip6tables_wait} -t mangle -F DIVERT 299 | ${ip6tables_wait} -t mangle -X DIVERT 300 | 301 | ${ip6tables_wait} -t mangle -F FILTER_OUT_CLASH 302 | ${ip6tables_wait} -t mangle -X FILTER_OUT_CLASH 303 | ${ip6tables_wait} -t mangle -F CLASH_OUT 304 | ${ip6tables_wait} -t mangle -X CLASH_OUT 305 | 306 | ${ip6tables_wait} -t mangle -F FILTER_PRE_CLASH 307 | ${ip6tables_wait} -t mangle -X FILTER_PRE_CLASH 308 | ${ip6tables_wait} -t mangle -F CLASH_PRE 309 | ${ip6tables_wait} -t mangle -X CLASH_PRE 310 | fi 311 | 312 | } 313 | 314 | set_tun(){ 315 | ip -4 rule add fwmark ${mark_id} table ${table_id} pref ${pref_id} 316 | while [ "$(ip -4 route show table ${table_id} 2> /dev/null)" == "" ] 317 | do 318 | ip -4 route add default dev ${tun_device} table ${table_id} 319 | done 320 | ${iptables_wait} -I FORWARD -o ${tun_device} -j ACCEPT 321 | ${iptables_wait} -I FORWARD -i ${tun_device} -j ACCEPT 322 | 323 | ${iptables_wait} -t mangle -N CLASH_OUT 324 | ${iptables_wait} -t mangle -A CLASH_OUT -m owner --uid-owner ${Clash_user} --gid-owner ${Clash_group} -j RETURN 325 | for subnet in ${reserved_ip[*]} ; do 326 | ${iptables_wait} -t mangle -A CLASH_OUT -d ${subnet} -j RETURN 327 | done 328 | ${scripts_dir}/clash.tool -f 329 | apps=`cat ${appuid_file} | sort -u` 330 | if [ "${mode}" = "blacklist" ] ; then 331 | for appuid in ${apps} ; do 332 | ${iptables_wait} -t mangle -A CLASH_OUT -m owner --uid-owner ${appuid} -j RETURN 333 | done 334 | ${iptables_wait} -t mangle -A CLASH_OUT -j MARK --set-xmark ${mark_id} 335 | elif [ "${mode}" = "whitelist" ] ; then 336 | for appuid in ${apps} ; do 337 | ${iptables_wait} -t mangle -A CLASH_OUT -m owner --uid-owner ${appuid} -j MARK --set-xmark ${mark_id} 338 | done 339 | fi 340 | ${iptables_wait} -t mangle -A OUTPUT -j CLASH_OUT 341 | 342 | ${iptables_wait} -t mangle -N CLASH_PRE 343 | for subnet in ${reserved_ip[*]} ; do 344 | ${iptables_wait} -t mangle -A CLASH_PRE -d ${subnet} -j RETURN 345 | done 346 | ${iptables_wait} -t mangle -A CLASH_PRE -j MARK --set-xmark ${mark_id} 347 | ${iptables_wait} -t mangle -A PREROUTING -j CLASH_PRE 348 | 349 | if [ "${ipv6}" = "true" ] ; then 350 | ip -6 rule add fwmark ${mark_id} table ${table_id} pref ${pref_id} 351 | while [ "$(ip -6 route show table ${table_id} 2> /dev/null)" == "" ] 352 | do 353 | ip -6 route add default dev ${tun_device} table ${table_id} 354 | done 355 | ${ip6tables_wait} -I FORWARD -o ${tun_device} -j ACCEPT 356 | ${ip6tables_wait} -I FORWARD -i ${tun_device} -j ACCEPT 357 | 358 | ${ip6tables_wait} -t mangle -N CLASH_OUT 359 | ${ip6tables_wait} -t mangle -A CLASH_OUT -m owner --uid-owner ${Clash_user} --gid-owner ${Clash_group} -j RETURN 360 | for subnet in ${reserved_ip6[*]} ; do 361 | ${ip6tables_wait} -t mangle -A CLASH_OUT -d ${subnet} -j RETURN 362 | done 363 | if [ "${mode}" = "blacklist" ] ; then 364 | for appuid in ${apps} ; do 365 | ${ip6tables_wait} -t mangle -A CLASH_OUT -m owner --uid-owner ${appuid} -j RETURN 366 | done 367 | ${ip6tables_wait} -t mangle -A CLASH_OUT -j MARK --set-xmark ${mark_id} 368 | elif [ "${mode}" = "whitelist" ] ; then 369 | for appuid in ${apps} ; do 370 | ${ip6tables_wait} -t mangle -A CLASH_OUT -m owner --uid-owner ${appuid} -j MARK --set-xmark ${mark_id} 371 | done 372 | fi 373 | ${ip6tables_wait} -t mangle -I OUTPUT -j CLASH_OUT 374 | 375 | ${ip6tables_wait} -t mangle -N CLASH_PRE 376 | for subnet in ${reserved_ip6[*]} ; do 377 | ${ip6tables_wait} -t mangle -A CLASH_PRE -d ${subnet} -j RETURN 378 | done 379 | ${ip6tables_wait} -t mangle -A CLASH_PRE -j MARK --set-xmark ${mark_id} 380 | ${ip6tables_wait} -t mangle -I PREROUTING -j CLASH_PRE 381 | else 382 | echo 1 > /proc/sys/net/ipv6/conf/${tun_device}/disable_ipv6 383 | echo dr 384 | ${ip6tables_wait} -t mangle -I OUTPUT -j DROP 385 | fi 386 | } 387 | 388 | del_tun(){ 389 | ip -4 rule del fwmark ${mark_id} lookup ${table_id} 390 | ip -4 route del default dev ${tun_device} table ${table_id} 391 | 392 | ${iptables_wait} -D FORWARD -o ${tun_device} -j ACCEPT 393 | ${iptables_wait} -D FORWARD -i ${tun_device} -j ACCEPT 394 | 395 | ${iptables_wait} -t mangle -D OUTPUT -j CLASH_OUT 396 | ${iptables_wait} -t mangle -F CLASH_OUT 397 | ${iptables_wait} -t mangle -X CLASH_OUT 398 | 399 | ${iptables_wait} -t mangle -D PREROUTING -j CLASH_PRE 400 | ${iptables_wait} -t mangle -F CLASH_PRE 401 | ${iptables_wait} -t mangle -X CLASH_PRE 402 | 403 | ip -6 rule del fwmark ${mark_id} lookup ${table_id} 404 | ip -6 route del default dev ${tun_device} table ${table_id} 405 | 406 | ${ip6tables_wait} -D FORWARD -o ${tun_device} -j ACCEPT 407 | ${ip6tables_wait} -D FORWARD -i ${tun_device} -j ACCEPT 408 | 409 | ${ip6tables_wait} -t mangle -D OUTPUT -j CLASH_OUT 410 | ${ip6tables_wait} -t mangle -F CLASH_OUT 411 | ${ip6tables_wait} -t mangle -X CLASH_OUT 412 | 413 | ${ip6tables_wait} -t mangle -D PREROUTING -j CLASH_PRE 414 | ${ip6tables_wait} -t mangle -F CLASH_PRE 415 | ${ip6tables_wait} -t mangle -X CLASH_PRE 416 | 417 | ${ip6tables_wait} -t mangle -D OUTPUT -j DROP 418 | } 419 | 420 | 421 | while getopts ":sk" signal ; do 422 | case ${signal} in 423 | s) 424 | if [ "${mode}" = "blacklist" ] || [ "${mode}" = "whitelist" ] ; then 425 | if [ "${Clash_tun_status}" == "true" ]; then 426 | echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"info: use Tun." >> ${CFM_logs_file} 427 | if [ "${Clash_auto_route}" == "true" ]; then 428 | ${iptables_wait} -I FORWARD -o ${tun_device} -j ACCEPT 429 | ${iptables_wait} -I FORWARD -i ${tun_device} -j ACCEPT 430 | ${ip6tables_wait} -I FORWARD -o ${tun_device} -j ACCEPT 431 | ${ip6tables_wait} -I FORWARD -i ${tun_device} -j ACCEPT 432 | exit 0 433 | else 434 | set_tun 435 | fi 436 | else 437 | echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"info: use tproxy." >> ${CFM_logs_file} 438 | create_rule_rules && apply_rules 439 | fi 440 | echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"info: iptables规则已应用." >> ${CFM_logs_file} 441 | else 442 | exit 0 443 | fi 444 | ;; 445 | k) 446 | if [ "${mode}" = "blacklist" ] || [ "${mode}" = "whitelist" ] ; then 447 | del_tun 448 | flush_rule_rules && flush_rules 449 | echo [`TZ=Asia/Shanghai date "+%H:%M:%S"`]"info: iptables规则已清空." >> ${CFM_logs_file} 450 | else 451 | exit 0 452 | fi 453 | ;; 454 | ?) 455 | echo "" 456 | ;; 457 | esac 458 | done 459 | --------------------------------------------------------------------------------