Merge branch 'dev' of github.com:juewuy/ShellCrash into dev

This commit is contained in:
juewuy
2026-10-24 10:38:59 +08:00
15 changed files with 713 additions and 95 deletions
+1 -1
View File
@@ -77,7 +77,7 @@ rm -rf "$CRASHDIR"/starts/shellcrash.openrc
command -v bash >/dev/null 2>&1 && shtype=bash
[ -x /bin/ash ] && shtype=ash
#批量授权
for file in start.sh starts/bfstart.sh starts/afstart.sh starts/fw_stop.sh menu.sh menus/task_cmd.sh menus/bot_tg.sh; do
for file in start.sh starts/bfstart.sh starts/afstart.sh starts/fw_stop.sh starts/fw_ipv6_watch.sh menu.sh menus/task_cmd.sh menus/bot_tg.sh; do
sed -i "s|/bin/sh|/bin/$shtype|" "$CRASHDIR/$file" 2>/dev/null
chmod +x "$CRASHDIR/$file" 2>/dev/null
done
+84 -30
View File
@@ -1,41 +1,95 @@
crondir="$(crond -h 2>&1 | grep -oE 'Default:.*' | awk -F ":" '{print $2}'| tr -d ' ')"
[ ! -w "$crondir" ] && crondir="/etc/storage/cron/crontabs"
[ ! -w "$crondir" ] && crondir="/var/spool/cron/crontabs"
[ ! -w "$crondir" ] && crondir="/var/spool/cron"
[ -z "$USER" ] && USER=$(whoami 2>/dev/null)
[ -z "$USER" ] && USER=$(id -un)
[ -z "$TASKCFGDIR" ] && TASKCFGDIR="$CRASHDIR"/configs/task
tmpcron=/tmp/cron_tmp
touch "$tmpcron"
cronadd() { #定时任务工具
if crontab -h 2>&1 | grep -q '\-l'; then
crontab "$1"
elif [ -w "$crondir" ];then
cat "$1" >"$crondir"/"$USER" && cru a REFRESH "0 0 1 1 * /bin/true" 2>/dev/null
else
echo "找不到可用的crond或者crontab应用!No available crond or crontab application can be found!"
fi
}
cronload() { #定时任务工具
if crontab -h 2>&1 | grep -q '\-l'; then
crontab -l
elif [ -f "$crondir/$USER" ];then
cat "$crondir"/"$USER" 2>/dev/null
else
# ShellCrash 使用的 ash/bash(以及 dash)均支持 test -O。
# shellcheck disable=SC3067
croncheck() { #在任何写操作前检查实例管理权限,保留普通用户自己的安装
cron_uid=$(id -u) || return 1
[ "$cron_uid" = 0 ] && return 0
if [ ! -O "$CRASHDIR/configs/ShellCrash.cfg" ] || [ ! -w "$CRASHDIR/configs/ShellCrash.cfg" ]; then
echo '无权管理此 ShellCrash 实例,请使用安装用户或 root。' >&2
return 1
fi
for cron_pid in $(pidof CrashCore 2>/dev/null); do
if [ ! -O "/proc/$cron_pid" ]; then
echo '无权管理其他用户启动的 CrashCore。' >&2
return 1
fi
done
}
cronset() { #定时任务设置
# 参数1代表要移除的关键字,参数2代表要添加的任务语句
cronload | grep -v '^$' | grep -vF "$1" >"$tmpcron"
[ -n "$2" ] && echo "$2" >>"$tmpcron"
cronadd "$tmpcron"
#华硕/Padavan固件存档在本地,其他则删除
if [ -d /jffs ] || [ -d /etc/storage/ShellCrash ];then
mkdir -p "$TASKCFGDIR"
mv -f "$tmpcron" "$TASKCFGDIR"/cron
cronadd() ( #定时任务工具;子 shell 隔离临时变量和清理 trap
croncheck || exit 1
[ -f "$1" ] && [ -r "$1" ] || exit 1
if crontab -h 2>&1 | grep -q '\-l'; then
crontab "$1"
elif [ -w "$crondir" ]; then
cron_user=$(id -un) || exit 1
cron_stage=$(mktemp "$crondir/.shellcrash.XXXXXX") || exit 1
trap 'rm -f "$cron_stage"' 0
trap 'exit 1' 1 2 3 15
[ ! -e "$crondir/$cron_user" ] || cp -p "$crondir/$cron_user" "$cron_stage" || exit 1
cat "$1" >"$cron_stage" && mv -f "$cron_stage" "$crondir/$cron_user" || exit 1
if command -v cru >/dev/null 2>&1; then
cru a REFRESH "0 0 1 1 * /bin/true"
fi
else
rm -f "$tmpcron"
echo '找不到可用的 crond 或 crontab!No available crond or crontab!' >&2
exit 1
fi
}
)
cronload() (
cron_user=$(id -un) || exit 1
if crontab -h 2>&1 | grep -q '\-l'; then
cron_text=$(LC_ALL=C crontab -l 2>&1)
cron_status=$?
if [ "$cron_status" = 0 ]; then
[ -z "$cron_text" ] || printf '%s\n' "$cron_text"
exit 0
fi
#首次使用没有 crontab 是合法空表,其他错误必须传回调用者。
case "$cron_text" in
"no crontab for $cron_user" | "crontab: can't open '$cron_user': No such file or directory") exit 0 ;;
esac
printf '%s\n' "$cron_text" >&2
exit "$cron_status"
elif [ -d "$crondir" ] && [ -r "$crondir" ] && [ -x "$crondir" ]; then
[ -e "$crondir/$cron_user" ] || [ -L "$crondir/$cron_user" ] || exit 0
cat "$crondir/$cron_user"
else
echo '无法读取原有 crontab,已取消修改。' >&2
exit 1
fi
)
cronupdate() ( #成功读取、生成后才提交;参数是读取 stdin 的过滤命令
croncheck || exit 1
cron_tmp=$(mktemp -d /tmp/ShellCrash-cron.XXXXXX) || exit 1
cron_saved=
trap 'rm -rf "$cron_tmp"; [ -z "$cron_saved" ] || rm -f "$cron_saved"' 0
trap 'exit 1' 1 2 3 15
cronload >"$cron_tmp/old" || exit 1
"$@" <"$cron_tmp/old" >"$cron_tmp/new" || exit 1
#先准备华硕/Padavan 的持久化副本;任何提交前的写入失败都保留原表。
if [ "$cron_persist" = true ] && { [ -d /jffs ] || [ -d /etc/storage/ShellCrash ]; }; then
mkdir -p "$TASKCFGDIR" || exit 1
cron_saved=$(mktemp "$TASKCFGDIR/.cron.XXXXXX") || exit 1
cat "$cron_tmp/new" >"$cron_saved" || exit 1
fi
cronadd "$cron_tmp/new" || exit 1
[ -z "$cron_saved" ] || mv -f "$cron_saved" "$TASKCFGDIR/cron"
)
cronset() ( #参数1:移除的文字;参数2:新增任务。空结果也是合法 crontab。
cron_persist=true
# shellcheck disable=SC2016
CRON_KEY="$1" CRON_TASK="$2" cronupdate awk '
NF && !index($0, ENVIRON["CRON_KEY"]) { print }
END { if (ENVIRON["CRON_TASK"] != "") print ENVIRON["CRON_TASK"] }
'
)
+14 -10
View File
@@ -23,7 +23,7 @@ set_cron() {
"0) $TASK5_NO"
read -r -p "$COMMON_INPUT> " res
if [ "$res" = '1' ]; then
task_txt="$min $hour * * $week $CRASHDIR/task/task.sh $task_id $cron_time$task_name"
task_txt="$min $hour * * $week $CRASHDIR/task/task.sh $task_id \"$cron_time$task_name\""
cronset "$cron_time$task_name" "$task_txt"
msg_alert -t 0 "$TASK5_TASK_PREFIX$cron_time$task_name$TASK5_TASK_ADDED"
fi
@@ -38,11 +38,11 @@ set_service() {
[ -s "$task_file" ] && sed -i "/$3/d" "$task_file"
# 运行时每分钟执行的任务特殊处理
if [ "$1" = "running" ]; then
task_txt="$4 $CRASHDIR/task/task.sh $2 $3"
task_txt="$4 $CRASHDIR/task/task.sh $2 \"$3\""
echo "$task_txt" >>"$task_file"
[ -n "$(pidof CrashCore)" ] && cronset "$3" "$task_txt"
else
echo "$CRASHDIR/task/task.sh $2 $3" >>"$task_file"
echo "$CRASHDIR/task/task.sh $2 \"$3\"" >>"$task_file"
fi
content_line "【$3】\033[32m$COMMON_SUCCESS\033[0m"
sleep 1
@@ -144,11 +144,12 @@ task_del() {
# 删除定时任务
cronset "$1"
# 删除条件任务
sed -i "/$1/d" "$TASKCFGDIR"/cron 2>/dev/null
sed -i "/$1/d" "$TASKCFGDIR"/bfstart 2>/dev/null
sed -i "/$1/d" "$TASKCFGDIR"/afstart 2>/dev/null
sed -i "/$1/d" "$TASKCFGDIR"/running 2>/dev/null
sed -i "/$1/d" "$TASKCFGDIR"/affirewall 2>/dev/null
[ -f "$TASKCFGDIR"/cron ] && sed -i "/$1/d" "$TASKCFGDIR"/cron 2>/dev/null
[ -f "$TASKCFGDIR"/bfstart ] && sed -i "/$1/d" "$TASKCFGDIR"/bfstart 2>/dev/null
[ -f "$TASKCFGDIR"/afstart ] && sed -i "/$1/d" "$TASKCFGDIR"/afstart 2>/dev/null
[ -f "$TASKCFGDIR"/running ] && sed -i "/$1/d" "$TASKCFGDIR"/running 2>/dev/null
[ -f "$TASKCFGDIR"/affirewall ] && sed -i "/$1/d" "$TASKCFGDIR"/affirewall 2>/dev/null
return 0
}
# 任务条件选择菜单
@@ -293,7 +294,10 @@ task_manager() {
break
fi
else
task_des=$(echo "$task_txt" | awk '{print $2}')
task_des=${task_txt#* }
task_des=${task_des#"${task_des%%[! ]*}"}
task_des=${task_des#\"}
task_des=${task_des%\"}
task_name=$(cat "$CRASHDIR"/task/task_${i18n}.list "$TASKCFGDIR"/task.user 2>/dev/null | grep "$task_id" | awk -F '#' '{print $3}')
comp_box "$TASK5_CURRENT_TASK\033[36m$task_des\033[0m"
btm_box "1) $TASK5_EDIT_TASK" \
@@ -359,7 +363,7 @@ task_recom() {
separator_line "="
set_service running "106" "$TASK_RECOM_ITEM_1" "*/10 * * * *"
set_service afstart "107" "$TASK_RECOM_ITEM_2"
cronset "$TASK_RECOM_ITEM_3" "0 3 * * * ${CRASHDIR}/task/task.sh 103 $TASK_RECOM_ITEM_3" &&
cronset "$TASK_RECOM_ITEM_3" "0 3 * * * ${CRASHDIR}/task/task.sh 103 \"$TASK_RECOM_ITEM_3\"" &&
content_line "【$TASK_RECOM_ITEM_3】\033[32m$COMMON_SUCCESS\033[0m"
separator_line "="
}
+10 -8
View File
@@ -6,10 +6,11 @@
cd $(dirname $0)
pwd
)
. "$CRASHDIR"/libs/set_cron.sh
croncheck || exit 1
. "$CRASHDIR"/libs/get_config.sh
#加载工具
. "$CRASHDIR"/libs/set_config.sh
. "$CRASHDIR"/libs/set_cron.sh
. "$CRASHDIR"/libs/check_cmd.sh
. "$CRASHDIR"/libs/check_autostart.sh
. "$CRASHDIR"/libs/compare.sh
@@ -36,7 +37,9 @@ start_l(){
case "$1" in
start)
[ -n "$(pidof CrashCore)" ] && $0 stop #禁止多实例
if [ -n "$(pidof CrashCore)" ]; then #禁止多实例
"$0" stop || exit 1
fi
stop_firewall #清理路由策略
rm -f "$CRASHDIR"/\.start_error #移除自启失败标记
#使用不同方式启动服务
@@ -69,9 +72,7 @@ stop)
logger ShellCrash服务即将关闭......
[ -n "$(pidof CrashCore)" ] && web_save #保存面板配置
#清理定时任务
cronload | grep -vE '^$|start_legacy_wd.sh|运行时每' > "$TMPDIR"/cron_tmp
cronadd "$TMPDIR"/cron_tmp
rm -f "$TMPDIR"/cron_tmp
cronupdate awk 'NF && !/start_legacy_wd.sh|运行时每/' || exit 1
#停止tg_bot
. "$CRASHDIR"/menus/bot_tg_service.sh && bot_tg_stop
#多种方式结束进程
@@ -96,8 +97,7 @@ stop)
rm -rf "$TMPDIR"/CrashCore
;;
restart)
$0 stop
$0 start
"$0" stop && "$0" start
;;
init)
. "$CRASHDIR"/starts/general_init.sh
@@ -110,7 +110,9 @@ daemon)
fi
;;
debug)
[ -n "$(pidof CrashCore)" ] && $0 stop >/dev/null #禁止多实例
if [ -n "$(pidof CrashCore)" ]; then #禁止多实例
"$0" stop >/dev/null || exit 1
fi
stop_firewall >/dev/null #清理路由策略
bfstart
if [ -n "$2" ]; then
+20 -9
View File
@@ -7,11 +7,12 @@
cd ..
pwd
)
. "$CRASHDIR"/libs/set_cron.sh
croncheck || exit 1
. "$CRASHDIR"/libs/get_config.sh
#加载工具
. "$CRASHDIR"/libs/check_cmd.sh
. "$CRASHDIR"/libs/logger.sh
. "$CRASHDIR"/libs/set_cron.sh
#缺省值
[ -z "$firewall_area" ] && firewall_area=1
#延迟启动
@@ -37,14 +38,24 @@ if [ -n "$test" -o -n "$(pidof CrashCore)" ]; then
} &
ckcmd mtd_storage.sh && mtd_storage.sh save >/dev/null 2>&1 #Padavan保存/etc/storage
#加载定时任务
cronload | grep -v '^$' >"$TMPDIR"/cron_tmp
[ -s "$TASKCFGDIR"/cron ] && cat "$TASKCFGDIR"/cron >>"$TMPDIR"/cron_tmp
[ -s "$TASKCFGDIR"/running ] && cat "$TASKCFGDIR"/running >>"$TMPDIR"/cron_tmp
[ "$bot_tg_service" = ON ] && echo "* * * * * /bin/sh $CRASHDIR/starts/start_legacy_wd.sh bot_tg #ShellCrash-TG_BOT守护进程" >>"$TMPDIR"/cron_tmp
[ "$start_old" = ON ] && echo "* * * * * /bin/sh $CRASHDIR/starts/start_legacy_wd.sh shellcrash #ShellCrash保守模式守护进程" >>"$TMPDIR"/cron_tmp
awk '!x[$0]++' "$TMPDIR"/cron_tmp >"$TMPDIR"/cron_tmp2 #删除重复行
cronadd "$TMPDIR"/cron_tmp2
rm -f "$TMPDIR"/cron_tmp "$TMPDIR"/cron_tmp2
set -- -
for cron_file in "$TASKCFGDIR/cron" "$TASKCFGDIR/running"; do
[ ! -e "$cron_file" ] || set -- "$@" "$cron_file"
done
# shellcheck disable=SC2016
CRON_BOT="$bot_tg_service" CRON_LEGACY="$start_old" CRON_DIR="$CRASHDIR" cronupdate awk '
NF && !seen[$0]++ { print }
END {
if (ENVIRON["CRON_BOT"] == "ON") {
line = "* * * * * /bin/sh " ENVIRON["CRON_DIR"] "/starts/start_legacy_wd.sh bot_tg #ShellCrash-TG_BOT守护进程"
if (!seen[line]++) print line
}
if (ENVIRON["CRON_LEGACY"] == "ON") {
line = "* * * * * /bin/sh " ENVIRON["CRON_DIR"] "/starts/start_legacy_wd.sh shellcrash #ShellCrash保守模式守护进程"
if (!seen[line]++) print line
}
}
' "$@" || exit 1
#加载条件任务
[ -s "$TASKCFGDIR"/afstart ] && { . "$TASKCFGDIR"/afstart; } &
[ -s "$TASKCFGDIR"/affirewall -a -s /etc/init.d/firewall -a ! -f /etc/init.d/firewall.bak ] && {
+24 -4
View File
@@ -7,7 +7,7 @@ prepare_clash_base_config() {
external="external-controller: 0.0.0.0:$db_port"
if [ "$redir_mod" = "Mix" -o "$redir_mod" = "Tun" ]; then
[ "$crashcore" = 'meta' ] && tun_meta=', device: utun, auto-route: false, auto-detect-interface: false'
tun="tun: {enable: true, stack: system$tun_meta}"
tun="tun: {enable: true, stack: mips$tun_meta}"
else
tun='tun: {enable: false}'
fi
@@ -216,6 +216,25 @@ add_custom_inbounds_and_rules() {
}
}
preserve_geodata_yaml() {
#保留订阅规则依赖的Geo数据设置,本地自定义同名配置优先
: >"$TMPDIR"/geodata_base.yaml
: >"$TMPDIR"/geodata.yaml
for char in geodata-mode geodata-loader geosite-matcher geo-auto-update geo-update-interval geox-url; do
awk -v key="$char" '
/^[^[:space:]#]/ { emit = ($0 ~ ("^" key ":")) }
emit { print }
' "$core_config" >>"$TMPDIR"/geodata_base.yaml
done
for char in geodata-mode geodata-loader geosite-matcher geo-auto-update geo-update-interval geox-url; do
grep -qE "^$char:" "$CRASHDIR"/yamls/user.yaml "$CRASHDIR"/yamls/others.yaml 2>/dev/null && continue
awk -v key="$char" '
/^[^[:space:]#]/ { emit = ($0 ~ ("^" key ":")) }
emit { print }
' "$TMPDIR"/geodata_base.yaml >>"$TMPDIR"/geodata.yaml
done
}
merger_yaml() {
#mix和route模式生成rule-providers
[ "$dns_mod" = "mix" ] || [ "$dns_mod" = "route" ] && ! grep -Eq '^[[:space:]]*cn:' "$TMPDIR"/rule-providers.yaml && ! grep -q '^rule-providers' "$CRASHDIR"/yamls/others.yaml 2>/dev/null && {
@@ -244,8 +263,9 @@ merger_yaml() {
yaml_add="$yaml_add $TMPDIR/${char}.yaml"
}
done
preserve_geodata_yaml
#合并完整配置文件
cut -c 1- "$TMPDIR"/set.yaml $yaml_dns $yaml_hosts $yaml_user $yaml_others $yaml_add >"$TMPDIR"/config.yaml
cut -c 1- "$TMPDIR"/set.yaml "$TMPDIR"/geodata.yaml $yaml_dns $yaml_hosts $yaml_user $yaml_others $yaml_add >"$TMPDIR"/config.yaml
}
test_yaml() {
@@ -259,7 +279,7 @@ test_yaml() {
sed -i "/#自定义策略组开始/,/#自定义策略组结束/d" "$TMPDIR"/proxy-groups.yaml
mv -f "$TMPDIR"/set_bak.yaml "$TMPDIR"/set.yaml >/dev/null 2>&1
#合并基础配置文件
cut -c 1- "$TMPDIR"/set.yaml $yaml_dns $yaml_add >"$TMPDIR"/config.yaml
cut -c 1- "$TMPDIR"/set.yaml "$TMPDIR"/geodata_base.yaml $yaml_dns $yaml_add >"$TMPDIR"/config.yaml
sed -i "/#自定义/d" "$TMPDIR"/config.yaml
fi
}
@@ -268,7 +288,7 @@ finalize_clash_yaml() {
#建立软连接
[ ""$TMPDIR"" = ""$BINDIR"" ] || ln -sf "$TMPDIR"/config.yaml "$BINDIR"/config.yaml 2>/dev/null || cp -f "$TMPDIR"/config.yaml "$BINDIR"/config.yaml
#清理缓存
for char in $yaml_char set set_bak dns hosts; do
for char in $yaml_char set set_bak dns hosts geodata geodata_base; do
rm -f "$TMPDIR"/${char}.yaml
done
}
+158 -21
View File
@@ -1,34 +1,171 @@
ipv6_route_prefixes() {
awk '
function is_global_prefix(value) {
return value ~ /^[23][0-9A-Fa-f]*:/ && value ~ /\/[0-9]+$/
}
$1 == "default" {
for (i = 1; i < NF; i++) {
if ($i == "from" && is_global_prefix($(i + 1)))
print $(i + 1)
}
next
}
is_global_prefix($1) {
via = 0
for (i = 1; i <= NF; i++) {
if ($i == "via")
via = 1
}
if (!via)
print $1
}'
}
get_lan_ifaces() {
ip route show scope link 2>/dev/null |
grep -Ev 'default|ppp|wan|utun|iot|peer|docker|podman|virbr|vnet|ovs|vmbr|veth|vmnic|vboxnet|lxcbr|xenbr|vEthernet|wgs|multicast|anycast' |
awk '{for(i=1;i<=NF;i++) if($i=="dev") {print $(i+1); break}}' |
grep -v '^lo$' |
sort -u
}
get_openwrt_lan_ifaces() {
if command -v ubus >/dev/null 2>&1; then
ubus -S call network.interface.lan status 2>/dev/null |
awk -F '"' '$2 == "l3_device" {print $4}'
fi
printf '%s\n' $lan_ifaces
}
get_ipv6_prefixes_openwrt() {
ipv6_prefixes=$(
for ipv6_iface in $(get_openwrt_lan_ifaces | sort -u); do
ip -6 route show dev "$ipv6_iface" 2>/dev/null |
grep -v '^default' |
ipv6_route_prefixes
done
)
# Some OpenWrt variants do not expose the LAN route through BusyBox ip.
# In that case, use netifd's source-specific default route as a fallback.
[ -n "$ipv6_prefixes" ] || ipv6_prefixes=$(
ip -6 route show default 2>/dev/null | ipv6_route_prefixes
)
printf '%s\n' "$ipv6_prefixes" | sort -u
}
get_ipv6_prefixes_linux() {
ipv6_prefixes=$(
for ipv6_iface in $lan_ifaces; do
ip -6 route show dev "$ipv6_iface" 2>/dev/null | grep -v '^default'
done | ipv6_route_prefixes | sort -u
)
# A host may use one interface for both its uplink and LAN, or may not
# have an IPv4 LAN route. Fall back to directly connected global routes.
[ -n "$ipv6_prefixes" ] || ipv6_prefixes=$(
ip -6 route show 2>/dev/null |
grep -Ev 'unreachable|prohibit|blackhole|ppp|wan|utun|iot|peer|docker|podman|virbr|vnet|ovs|vmbr|veth|vmnic|vboxnet|lxcbr|xenbr|vEthernet|wgs|multicast|anycast' |
ipv6_route_prefixes |
sort -u
)
printf '%s\n' "$ipv6_prefixes"
}
get_ipv6_prefixes_router() {
# Padavan/Asuswrt and other BusyBox based firmware do not consistently
# expose route protocol metadata. Only depend on route shape and devices.
ipv6_prefixes=$(
for ipv6_iface in $lan_ifaces; do
ip -6 route show dev "$ipv6_iface" 2>/dev/null |
grep -v '^default' |
ipv6_route_prefixes
done
)
[ -n "$ipv6_prefixes" ] || ipv6_prefixes=$(
ip -6 route show default 2>/dev/null | ipv6_route_prefixes
)
printf '%s\n' "$ipv6_prefixes" | sort -u
}
get_ipv6_prefixes_fallback() {
# Generic fallback for systems unknown to ShellCrash. Avoid fixed field
# positions and route protocol names, which differ across ip variants.
ip -6 route show 2>/dev/null | ipv6_route_prefixes | sort -u
}
get_ipv6_platform() {
case "$systype" in
mi_snapshot | ng_snapshot)
echo openwrt
return
;;
Padavan | asusrouter)
echo router
return
;;
container)
echo linux
return
;;
esac
if [ -f /etc/openwrt_release ] || { [ -f /etc/rc.common ] && command -v uci >/dev/null 2>&1; }; then
echo openwrt
elif [ "$(uname -s 2>/dev/null)" = Linux ]; then
echo linux
else
echo fallback
fi
}
get_ipv6_prefixes() {
[ -n "$lan_ifaces" ] || lan_ifaces=$(get_lan_ifaces)
case "$(get_ipv6_platform)" in
openwrt) get_ipv6_prefixes_openwrt ;;
router) get_ipv6_prefixes_router ;;
linux) get_ipv6_prefixes_linux ;;
*) get_ipv6_prefixes_fallback ;;
esac
}
build_host_ipv6() {
host_ipv6="fe80::/10 fd00::/8 $1"
[ "$ts_service" = ON ] && host_ipv6="$host_ipv6 fd7a:115c:a1e0::/48"
if [ "$wg_service" = ON ]; then
[ -f "$CRASHDIR"/configs/gateway.cfg ] && . "$CRASHDIR"/configs/gateway.cfg
[ -n "$wg_ipv6" ] && host_ipv6="$host_ipv6 $wg_ipv6"
fi
# Only pass prefix-shaped values to firewall generators and keep the
# result stable so the watcher can compare it cheaply.
host_ipv6=$(printf '%s\n' "$host_ipv6" |
tr ' ' '\n' |
awk '/^[0-9A-Fa-f:]+\/[0-9]+$/ {print}' |
sort -u |
tr '\n' ' ' |
sed 's/ $//')
}
getlanip() { #获取局域网host地址
i=1
while [ "$i" -le "20" ]; do
#ipv4局域网网段
host_ipv4=$(ip route show scope link | grep -Ev 'default|wan|utun|iot|peer|docker|podman|virbr|vnet|ovs|vmbr|veth|vmnic|vboxnet|lxcbr|xenbr|vEthernet|wgs|multicast|anycast' | awk '{print $1}')
#ipv6局域网网段 - 从IPv4已识别的LAN接口获取全局IPv6前缀
[ "$ipv6_redir" = "ON" ] && {
lan_ifaces=$(ip route show scope link | grep -Ev 'ppp|wan|utun|iot|peer|docker|podman|virbr|vnet|ovs|vmbr|veth|vmnic|vboxnet|lxcbr|xenbr|vEthernet|wgs|multicast|anycast' | awk '{for(i=1;i<=NF;i++) if($i=="dev") {print $(i+1); break}}' | grep -v '^lo$' | sort -u)
host_ipv6=$(
for iface in $lan_ifaces; do
ip -6 addr show dev $iface 2>/dev/null
done | grep 'scope global' | awk '{print $2}' | tr '\n' ' ' | sed 's/ $//'
)
[ -z "$host_ipv6" ] && host_ipv6=$(ip -6 route show | grep -Ev 'default|unreachable|fe80::/|wan|ppp|utun|iot|peer|docker|podman|virbr|vnet|ovs|vmbr|veth|vmnic|vboxnet|lxcbr|xenbr|vEthernet|wgs|multicast|anycast' | awk '{print $1}' | tr '\n' ' ' | sed 's/ $//')
}
#ipv6局域网网段
if [ "$ipv6_redir" = "ON" ]; then
lan_ifaces=$(get_lan_ifaces)
detected_host_ipv6=$(get_ipv6_prefixes)
fi
[ -f "$TMPDIR"/ShellCrash.log ] && break
[ -n "$host_ipv4" -a "$ipv6_redir" != "ON" ] && break
[ -n "$host_ipv4" -a -n "$host_ipv6" ] && break
[ -n "$host_ipv4" ] && [ "$ipv6_redir" != "ON" ] && break
[ -n "$host_ipv4" ] && [ -n "$detected_host_ipv6" ] && break
sleep 1 && i=$((i + 1))
done
#Tailscale
[ "$ts_service" = ON ] && {
ts_host_ipv4=' 100.64.0.0/10'
ts_host_ipv6=' fd7a:115c:a1e0::/48'
}
[ "$ts_service" = ON ] && ts_host_ipv4=' 100.64.0.0/10'
#Wireguard
[ "$wg_service" = ON ] && {
if [ "$wg_service" = ON ]; then
. "$CRASHDIR"/configs/gateway.cfg
wg_host_ipv4=' $wg_ipv4'
[ -n "$wg_ipv6" ] && wg_host_ipv6=' $wg_ipv6'
}
wg_host_ipv4=" $wg_ipv4"
fi
#添加自定义ipv4局域网网段
if [ "$replace_default_host_ipv4" = "ON" ]; then
host_ipv4="$cust_host_ipv4"
@@ -40,7 +177,7 @@ getlanip() { #获取局域网host地址
host_ipv4='192.168.0.0/16 10.0.0.0/12 172.16.0.0/12'
logger "无法获取本地LAN-IPV4网段,请前往流量过滤设置界面设置自定义网段!" 31
}
host_ipv6="fe80::/10 fd00::/8 $host_ipv6$ts_host_ipv6$wg_host_ipv6"
build_host_ipv6 "$detected_host_ipv6"
#获取本机出口IP地址
local_ipv4=$(ip route 2>&1 | grep -Ev 'utun|iot|docker|linkdown' | grep -Eo 'src.*' | grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | sort -u)
[ -z "$local_ipv4" ] && local_ipv4=$(ip route 2>&1 | grep -Eo 'src.*' | grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | sort -u)
+123
View File
@@ -0,0 +1,123 @@
#!/bin/sh
# Keep the nftables LAN IPv6 prefix set in sync with dynamic prefixes.
[ -z "$CRASHDIR" ] && CRASHDIR=$(
cd "$(dirname "$0")"
cd ..
pwd
)
. "$CRASHDIR"/libs/get_config.sh
. "$CRASHDIR"/starts/fw_getlanip.sh
[ -z "$TMPDIR" ] && TMPDIR=/tmp/ShellCrash
ipv6_watch_pid="$TMPDIR/ipv6_watch.pid"
ipv6_watch_fifo="$TMPDIR/ipv6_route_monitor.$$"
ipv6_nft_family=${SHELLCRASH_NFT_FAMILY:-inet}
ipv6_nft_table=${SHELLCRASH_NFT_TABLE:-shellcrash}
ipv6_nft_set=${SHELLCRASH_IPV6_SET:-lan_ip6}
stop_ipv6_watch() {
[ -s "$ipv6_watch_pid" ] || return 0
ipv6_watch_old_pid=$(cat "$ipv6_watch_pid" 2>/dev/null)
case "$ipv6_watch_old_pid" in
'' | *[!0-9]*) ;;
*)
if [ ! -r "/proc/$ipv6_watch_old_pid/cmdline" ] ||
tr '\0' ' ' <"/proc/$ipv6_watch_old_pid/cmdline" 2>/dev/null | grep -q 'fw_ipv6_watch.sh'; then
kill "$ipv6_watch_old_pid" 2>/dev/null
fi
;;
esac
rm -f "$ipv6_watch_pid"
}
cleanup_ipv6_watch() {
trap - EXIT INT TERM
[ -n "$ipv6_monitor_pid" ] && kill "$ipv6_monitor_pid" 2>/dev/null
[ -p "$ipv6_watch_fifo" ] && rm -f "$ipv6_watch_fifo"
if [ "$(cat "$ipv6_watch_pid" 2>/dev/null)" = "$$" ]; then
rm -f "$ipv6_watch_pid"
fi
exit 0
}
update_ipv6_set() {
lan_ifaces=$(get_lan_ifaces)
detected_host_ipv6=$(get_ipv6_prefixes)
build_host_ipv6 "$detected_host_ipv6"
ipv6_watch_state="$host_ipv6"
if [ "$ipv6_watch_state" = "$ipv6_watch_last_state" ] &&
nft list set "$ipv6_nft_family" "$ipv6_nft_table" "$ipv6_nft_set" >/dev/null 2>&1; then
return 0
fi
ipv6_watch_elements=$(echo "$host_ipv6" | sed 's/[[:space:]]\+/, /g')
[ -n "$ipv6_watch_elements" ] || return 1
nft list set "$ipv6_nft_family" "$ipv6_nft_table" "$ipv6_nft_set" >/dev/null 2>&1 || return 1
# nft processes a batch atomically, so packets never observe a partially
# updated set while an ISP prefix is being replaced.
printf 'flush set %s %s %s\nadd element %s %s %s { %s }\n' \
"$ipv6_nft_family" "$ipv6_nft_table" "$ipv6_nft_set" \
"$ipv6_nft_family" "$ipv6_nft_table" "$ipv6_nft_set" "$ipv6_watch_elements" |
nft -f - >/dev/null 2>&1 || return 1
ipv6_watch_last_state="$ipv6_watch_state"
}
run_iproute2_monitor() {
rm -f "$ipv6_watch_fifo"
mkfifo "$ipv6_watch_fifo" 2>/dev/null || return 1
ip -6 monitor route >"$ipv6_watch_fifo" 2>/dev/null &
ipv6_monitor_pid=$!
while IFS= read -r _ipv6_route_event; do
# Coalesce delete/add notifications emitted during renumbering.
sleep 1
update_ipv6_set
done <"$ipv6_watch_fifo"
wait "$ipv6_monitor_pid" 2>/dev/null
ipv6_monitor_pid=
rm -f "$ipv6_watch_fifo"
return 1
}
run_ipv6_watch() {
mkdir -p "$TMPDIR"
echo $$ >"$ipv6_watch_pid"
trap cleanup_ipv6_watch EXIT INT TERM
ipv6_watch_last_state=
update_ipv6_set
# Full iproute2 has event monitoring. BusyBox/router variants that do not
# support it use a small polling fallback instead.
if ip -Version 2>&1 | grep -qi 'iproute2'; then
run_iproute2_monitor
fi
while :; do
sleep 30
update_ipv6_set
done
}
case "$1" in
start)
mkdir -p "$TMPDIR"
stop_ipv6_watch
/bin/sh "$0" run >/dev/null 2>&1 &
echo $! >"$ipv6_watch_pid"
;;
stop)
stop_ipv6_watch
;;
run)
run_ipv6_watch
;;
once)
ipv6_watch_last_state=
update_ipv6_set
;;
*)
echo "Usage: $0 {start|stop|run|once}" >&2
exit 1
;;
esac
+16 -10
View File
@@ -2,13 +2,22 @@
# Copyright (C) Juewuy
RESERVED_IP=$(echo $reserve_ipv4 | sed 's/[[:space:]]\+/, /g')
RESERVED_IP6=$(echo "$reserve_ipv6 $host_ipv6" | sed 's/[[:space:]]\+/, /g')
RESERVED_IP6=$(echo "$reserve_ipv6" | sed 's/[[:space:]]\+/, /g')
start_nft_ipv6_set() {
HOST_IP6=$(echo "$host_ipv6" | sed 's/[[:space:]]\+/, /g')
nft list set inet shellcrash lan_ip6 >/dev/null 2>&1 ||
nft add set inet shellcrash lan_ip6 { type ipv6_addr \; flags interval \; auto-merge \; }
nft flush set inet shellcrash lan_ip6 2>/dev/null
[ -n "$HOST_IP6" ] && nft add element inet shellcrash lan_ip6 { $HOST_IP6 }
}
add_ip6_route(){
#过滤保留地址及本机地址
#过滤保留地址及当前局域网地址
nft add rule inet shellcrash $1 ip6 daddr {$RESERVED_IP6} return
#仅代理本机局域网网段流量
nft add rule inet shellcrash $1 ip6 saddr != {$HOST_IP6} return
nft add rule inet shellcrash $1 ip6 daddr @lan_ip6 return
#OUTPUT只包含本机流量,无需依赖动态公网源前缀
[ "$1" = 'prerouting' ] && nft add rule inet shellcrash $1 ip6 saddr != @lan_ip6 return
#绕过CN_IPV6
[ "$dns_mod" != "fake-ip" -a "$cn_ip_route" = "ON" -a -f "$BINDIR"/cn_ipv6.txt ] && {
CN_IP6=$(awk '{printf "%s, ",$1}' "$BINDIR"/cn_ipv6.txt)
@@ -83,10 +92,8 @@ start_nft_route() { #nftables-route通用工具
}
#局域网ipv6支持
if [ "$ipv6_redir" = "ON" -a "$1" = 'prerouting' -a "$firewall_area" != 5 ]; then
HOST_IP6=$(echo $host_ipv6 | sed 's/[[:space:]]\+/, /g')
add_ip6_route "$1"
elif [ "$ipv6_redir" = "ON" -a "$1" = 'output' -a \( "$firewall_area" = 2 -o "$firewall_area" = 3 \) ]; then
HOST_IP6="::1, $(echo $host_ipv6 | sed 's/[[:space:]]\+/, /g')"
add_ip6_route "$1"
else
nft add rule inet shellcrash $1 meta nfproto ipv6 return
@@ -106,7 +113,6 @@ start_nft_route() { #nftables-route通用工具
start_nft_dns() { #nftables-dns
[ "$1" = 'prerouting' ] && {
HOST_IP=$(echo $host_ipv4 | sed 's/[[:space:]]\+/, /g')
HOST_IP6=$(echo $host_ipv6 | sed 's/[[:space:]]\+/, /g')
}
[ "$1" = 'output' ] && HOST_IP="127.0.0.0/8, $(echo $local_ipv4 | sed 's/[[:space:]]\+/, /g')"
[ "$1" = 'prerouting_vm' ] && HOST_IP="$(echo $vm_ipv4 | sed 's/[[:space:]]\+/, /g')"
@@ -119,7 +125,7 @@ start_nft_dns() { #nftables-dns
nft add rule inet shellcrash "$1"_dns meta skgid { 453, 7890 } return
[ "$firewall_area" = 5 ] && nft add rule inet shellcrash "$1"_dns ip saddr $bypass_host return
nft add rule inet shellcrash "$1"_dns ip saddr != {$HOST_IP} return #屏蔽外部请求
[ "$1" = 'prerouting' ] && nft add rule inet shellcrash "$1"_dns ip6 saddr != {$HOST_IP6} return #屏蔽外部请求
[ "$1" = 'prerouting' ] && nft add rule inet shellcrash "$1"_dns ip6 saddr != @lan_ip6 return #屏蔽外部请求
#过滤局域网设备
[ "$1" = 'prerouting' ] && [ -s "$CRASHDIR"/configs/mac ] && {
MAC=$(awk '{printf "%s, ",$1}' "$CRASHDIR"/configs/mac)
@@ -134,7 +140,6 @@ start_nft_dns() { #nftables-dns
}
start_nft_wan() { #nftables公网防火墙
HOST_IP=$(echo $host_ipv4 | sed 's/[[:space:]]\+/, /g')
HOST_IP6=$(echo $host_ipv6 | sed 's/[[:space:]]\+/, /g')
nft add chain inet shellcrash input { type filter hook input priority -100 \; }
nft add rule inet shellcrash input iif lo accept #本机请求全放行
#端口放行
@@ -148,7 +153,7 @@ start_nft_wan() { #nftables公网防火墙
#端口拦截
reject_ports="{ $mix_port, $db_port }"
nft add rule inet shellcrash input ip saddr {$HOST_IP} accept
nft add rule inet shellcrash input ip6 saddr {$HOST_IP6} accept
nft add rule inet shellcrash input ip6 saddr @lan_ip6 accept
nft add rule inet shellcrash input tcp dport $reject_ports reject
nft add rule inet shellcrash input udp dport $reject_ports reject
#fw4特殊处理
@@ -160,6 +165,7 @@ start_nftables() { #nftables配置总入口
#初始化nftables
nft add table inet shellcrash 2>/dev/null
nft flush table inet shellcrash 2>/dev/null
start_nft_ipv6_set
#公网访问防火墙
[ "$fw_wan" != OFF ] && [ "$systype" != 'container' ] && start_nft_wan
#启动DNS劫持
+5
View File
@@ -1,6 +1,8 @@
#!/bin/sh
# Copyright (C) Juewuy
#停止旧的IPv6前缀监听,避免防火墙重建期间并发更新
[ -f "$CRASHDIR"/starts/fw_ipv6_watch.sh ] && /bin/sh "$CRASHDIR"/starts/fw_ipv6_watch.sh stop
#获取局域网host地址
. "$CRASHDIR"/starts/fw_getlanip.sh && getlanip
#缺省值
@@ -46,6 +48,9 @@
#防火墙配置
[ "$firewall_mod" = 'iptables' ] && . "$CRASHDIR"/starts/fw_iptables.sh && start_iptables
[ "$firewall_mod" = 'nftables' ] && . "$CRASHDIR"/starts/fw_nftables.sh && start_nftables
#动态更新nftables中的IPv6局域网前缀集合
[ "$firewall_mod" = 'nftables' -a "$ipv6_redir" = "ON" ] &&
/bin/sh "$CRASHDIR"/starts/fw_ipv6_watch.sh start
#修复部分虚拟机dns查询失败的问题
[ "$firewall_area" = 2 -o "$firewall_area" = 3 ] && [ -z "$(grep '127.0.0.1' /etc/resolv.conf 2>/dev/null)" ] && [ "$systype" != 'container' ] && {
line=$(grep -n 'nameserver' /etc/resolv.conf | awk -F: 'FNR==1{print $1}')
+2 -1
View File
@@ -5,6 +5,8 @@
[ -z "$CRASHDIR" ] && CRASHDIR=$( cd $(dirname $0);cd ..;pwd)
. "$CRASHDIR"/libs/get_config.sh
. "$CRASHDIR"/libs/check_cmd.sh
#停止IPv6前缀监听
[ -f "$CRASHDIR"/starts/fw_ipv6_watch.sh ] && /bin/sh "$CRASHDIR"/starts/fw_ipv6_watch.sh stop
. "$CRASHDIR"/starts/fw_getlanip.sh && getlanip #获取局域网host地址
#缺省值
[ -z "$common_ports" ] && common_ports='ON'
@@ -172,4 +174,3 @@ ckcmd nft && nft delete table inet shellcrash >/dev/null 2>&1
[ -s /etc/init.d/firewall.bak ] && mv -f /etc/init.d/firewall.bak /etc/init.d/firewall
#others
sed -i '/shellcrash-dns-repair/d' /etc/resolv.conf 2>/dev/null
+2 -1
View File
@@ -4,7 +4,6 @@
CRASHDIR="$(uci get firewall.ShellCrash.path | sed 's/\/starts.*//')"
#防止提前自启
/etc/init.d/shellcrash disable
crontab -l | grep -v 'start_legacy_wd.sh shellcrash' | crontab -
#防止usb未加载
i=0
while [ ! -f "$CRASHDIR/configs/ShellCrash.cfg" ]; do
@@ -13,6 +12,8 @@ while [ ! -f "$CRASHDIR/configs/ShellCrash.cfg" ]; do
sleep 3
done
. "$CRASHDIR"/configs/ShellCrash.cfg
. "$CRASHDIR"/libs/set_cron.sh
cronset 'start_legacy_wd.sh shellcrash' || exit 1
autoSSH() {
#自动开启SSH
+93
View File
@@ -0,0 +1,93 @@
#!/bin/sh
# Run with sh tests/clash_geodata.sh [path/to/clash_modify.sh].
set -e
script=${1:-$(dirname "$0")/../scripts/starts/clash_modify.sh}
. "$script"
test_dir=$(mktemp -d)
trap 'rm -rf "$test_dir"' EXIT HUP INT TERM
setup() {
CRASHDIR="$test_dir/$1"
TMPDIR="$CRASHDIR/tmp"
BINDIR="$CRASHDIR/bin"
mkdir -p "$CRASHDIR/yamls" "$TMPDIR" "$BINDIR"
core_config="$CRASHDIR/yamls/config.yaml"
dns_mod=redir_host
yaml_char='rules proxy-groups'
yaml_user= yaml_others= yaml_dns= yaml_hosts=
printf 'mode: Rule\n' >"$TMPDIR/set.yaml"
printf ' - GEOSITE,GOOGLE,DIRECT\n' >"$TMPDIR/rules.yaml"
: >"$TMPDIR/proxy-groups.yaml"
cat >"$core_config" <<'EOF'
geodata-mode: true
geodata-loader: memconservative
geosite-matcher: succinct
geo-auto-update: true
geo-update-interval: 24
geox-url:
geoip: https://example.invalid/geoip.dat
geosite: >-
https://example.invalid/geosite.dat
mmdb: https://example.invalid/Country.mmdb
rules:
- GEOSITE,GOOGLE,DIRECT
mixed-port: 1234
EOF
}
assert_line() {
grep -qFx -- "$1" "$TMPDIR/config.yaml" || {
echo "Missing expected line: $1" >&2
exit 1
}
}
setup inherited
merger_yaml
assert_line 'geodata-mode: true'
assert_line 'geodata-loader: memconservative'
assert_line 'geosite-matcher: succinct'
assert_line 'geo-auto-update: true'
assert_line 'geo-update-interval: 24'
assert_line ' https://example.invalid/geosite.dat'
[ "$(grep -c '^rules:' "$TMPDIR/config.yaml")" = 1 ]
! grep -q '^mixed-port: 1234' "$TMPDIR/config.yaml"
finalize_clash_yaml
[ ! -e "$TMPDIR/geodata.yaml" ]
[ ! -e "$TMPDIR/geodata_base.yaml" ]
setup overrides
printf 'geodata-mode: false\n' >"$CRASHDIR/yamls/user.yaml"
printf 'geox-url: {geosite: https://override.invalid/geosite.dat}\n' >"$CRASHDIR/yamls/others.yaml"
merger_yaml
assert_line 'geodata-mode: false'
assert_line 'geox-url: {geosite: https://override.invalid/geosite.dat}'
[ "$(grep -c '^geodata-mode:' "$TMPDIR/config.yaml")" = 1 ]
[ "$(grep -c '^geox-url:' "$TMPDIR/config.yaml")" = 1 ]
! grep -q 'example.invalid' "$TMPDIR/config.yaml"
# A rejected custom configuration must fall back to the subscription settings.
printf '#!/bin/sh\nexit 1\n' >"$TMPDIR/CrashCore"
chmod +x "$TMPDIR/CrashCore"
logger() { :; }
set +e
test_yaml
set -e
assert_line 'geodata-mode: true'
assert_line ' https://example.invalid/geosite.dat'
[ "$(grep -c '^geox-url:' "$TMPDIR/config.yaml")" = 1 ]
! grep -q 'override.invalid' "$TMPDIR/config.yaml"
setup inline
printf 'geox-url: {geosite: https://inline.invalid/geosite.dat}\ngeo-auto-update: false\n---\nmixed-port: 1234\n' >"$core_config"
merger_yaml
assert_line 'geox-url: {geosite: https://inline.invalid/geosite.dat}'
assert_line 'geo-auto-update: false'
! grep -qE '^---|^mixed-port: 1234' "$TMPDIR/config.yaml"
setup absent
printf 'rules:\n - MATCH,DIRECT\n' >"$core_config"
merger_yaml
! grep -qE '^geo(data|site|x|-)' "$TMPDIR/config.yaml"
assert_line 'mode: Rule'
echo 'PASS: inherited geodata, overrides, fallback, inline maps, cleanup and absent settings'
+83
View File
@@ -0,0 +1,83 @@
#!/bin/sh
set -eu
ROOT=$(cd "$(dirname "$0")/.." && pwd)
. "$ROOT/scripts/starts/fw_getlanip.sh"
assert_equal() {
if [ "$1" != "$2" ]; then
echo "expected: [$1]" >&2
echo "actual: [$2]" >&2
exit 1
fi
}
route_output='default via fe80::1 dev eth0
default from 240e:1234:5600::/56 via fe80::1 dev pppoe-wan
2408:8266:501:2443::/64 dev enp2s0 proto kernel
2606:4700::/48 via fe80::2 dev eth0
unreachable 240e:1234:5600::/48 dev lo'
actual=$(printf '%s\n' "$route_output" | ipv6_route_prefixes | sort -u)
expected='2408:8266:501:2443::/64
240e:1234:5600::/56'
assert_equal "$expected" "$actual"
# OpenWrt prefers the LAN assignment over the wider delegated prefix.
systype=mi_snapshot
lan_ifaces='br-lan'
ubus() {
printf '%s\n' '{ "l3_device": "br-lan" }'
}
ip() {
case "$*" in
'-6 route show dev br-lan')
echo 'default from 240e:1234:5600::/56 via fe80::1 dev br-lan'
echo '240e:1234:5600:1::/64 dev br-lan proto static'
;;
'-6 route show default') echo 'default from 240e:1234:5600::/56 via fe80::1 dev pppoe-wan' ;;
esac
}
actual=$(get_ipv6_prefixes)
assert_equal '240e:1234:5600:1::/64' "$actual"
# OpenWrt falls back to source-specific routes when its LAN route is hidden.
lan_ifaces=
ubus() {
return 0
}
ip() {
case "$*" in
'-6 route show default') echo 'default from 240e:1234:5600::/56 via fe80::1 dev pppoe-wan' ;;
esac
}
actual=$(get_ipv6_prefixes)
assert_equal '240e:1234:5600::/56' "$actual"
# Standard Linux uses the directly connected route on the discovered LAN.
systype=container
lan_ifaces='enp2s0'
ip() {
case "$*" in
'-6 route show dev enp2s0') echo '2408:8266:501:2443::/64 dev enp2s0 proto kernel metric 256' ;;
esac
}
actual=$(get_ipv6_prefixes)
assert_equal '2408:8266:501:2443::/64' "$actual"
# BusyBox based router firmware does not need route protocol metadata.
systype=Padavan
lan_ifaces='br0'
ip() {
case "$*" in
'-6 route show dev br0') echo '2001:db8:abcd:1::/64 dev br0 metric 256' ;;
esac
}
actual=$(get_ipv6_prefixes)
assert_equal '2001:db8:abcd:1::/64' "$actual"
ts_service=ON
wg_service=OFF
build_host_ipv6 '2408:8266:501:2443::/64 invalid-value'
assert_equal '2408:8266:501:2443::/64 fd00::/8 fd7a:115c:a1e0::/48 fe80::/10' "$host_ipv6"
echo 'IPv6 prefix tests passed'
+78
View File
@@ -0,0 +1,78 @@
#!/bin/sh
set -eu
ROOT=$(cd "$(dirname "$0")/.." && pwd)
reserve_ipv4='0.0.0.0/8 127.0.0.0/8'
reserve_ipv6='::/128 ::1/128 fe80::/10 ff00::/8'
host_ipv4='192.168.1.0/24'
local_ipv4='192.168.1.1'
host_ipv6='2001:db8:1::/64 fd00::/8 fe80::/10'
common_ports=OFF
ports=
PORTS=
mix_port=7890
redir_port=7892
tproxy_port=7893
dns_redir_port=1053
routing_mark=7894
fwmark=7892
firewall_area=3
macfilter_type='黑名单'
cn_ip_route=OFF
dns_mod=route
ipv6_redir=ON
quic_rj=OFF
lan_proxy=true
local_proxy=true
redir_mod=Tproxy
tun_statu=false
vm_redir=OFF
fw_wan=ON
fw_wan_ports=
vms_port=
sss_port=
db_port=9090
systype=linux
CRASHDIR=/nonexistent
BINDIR=/nonexistent
nft() {
if [ "$1" = list ] && [ "$2" = set ]; then
return 1
fi
printf '%s' "$1"
shift
printf ' %s' "$@"
printf '\n'
}
modprobe() {
return 0
}
. "$ROOT/scripts/starts/fw_nftables.sh"
rules=$(start_nftables || :)
require_rule() {
if ! printf '%s\n' "$rules" | grep -Fq "$1"; then
echo "missing generated rule: $1" >&2
exit 1
fi
}
require_rule 'add set inet shellcrash lan_ip6'
require_rule 'flags interval ; auto-merge'
require_rule 'add element inet shellcrash lan_ip6'
require_rule 'add rule inet shellcrash prerouting ip6 daddr @lan_ip6 return'
require_rule 'add rule inet shellcrash prerouting ip6 saddr != @lan_ip6 return'
require_rule 'add rule inet shellcrash prerouting_dns ip6 saddr != @lan_ip6 return'
require_rule 'add rule inet shellcrash input ip6 saddr @lan_ip6 accept'
require_rule 'add rule inet shellcrash output ip6 daddr @lan_ip6 return'
if printf '%s\n' "$rules" | grep -Fq 'add rule inet shellcrash output ip6 saddr'; then
echo 'output must not depend on a dynamic IPv6 source prefix' >&2
exit 1
fi
echo 'nftables IPv6 rule generation tests passed'