mirror of
https://github.com/juewuy/ShellCrash.git
synced 2026-10-24 11:49:20 +00:00
Merge pull request #1345 from ctaoist/feat/dynamic-ipv6-prefix
修复 IPv6 前缀变化后 TProxy 规则未刷新
This commit is contained in:
+1
-1
@@ -77,7 +77,7 @@ rm -rf "$CRASHDIR"/starts/shellcrash.openrc
|
|||||||
command -v bash >/dev/null 2>&1 && shtype=bash
|
command -v bash >/dev/null 2>&1 && shtype=bash
|
||||||
[ -x /bin/ash ] && shtype=ash
|
[ -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
|
sed -i "s|/bin/sh|/bin/$shtype|" "$CRASHDIR/$file" 2>/dev/null
|
||||||
chmod +x "$CRASHDIR/$file" 2>/dev/null
|
chmod +x "$CRASHDIR/$file" 2>/dev/null
|
||||||
done
|
done
|
||||||
|
|||||||
+158
-21
@@ -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地址
|
getlanip() { #获取局域网host地址
|
||||||
i=1
|
i=1
|
||||||
while [ "$i" -le "20" ]; do
|
while [ "$i" -le "20" ]; do
|
||||||
#ipv4局域网网段
|
#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}')
|
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局域网网段
|
||||||
[ "$ipv6_redir" = "ON" ] && {
|
if [ "$ipv6_redir" = "ON" ]; then
|
||||||
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)
|
lan_ifaces=$(get_lan_ifaces)
|
||||||
host_ipv6=$(
|
detected_host_ipv6=$(get_ipv6_prefixes)
|
||||||
for iface in $lan_ifaces; do
|
fi
|
||||||
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/ $//')
|
|
||||||
}
|
|
||||||
[ -f "$TMPDIR"/ShellCrash.log ] && break
|
[ -f "$TMPDIR"/ShellCrash.log ] && break
|
||||||
[ -n "$host_ipv4" -a "$ipv6_redir" != "ON" ] && break
|
[ -n "$host_ipv4" ] && [ "$ipv6_redir" != "ON" ] && break
|
||||||
[ -n "$host_ipv4" -a -n "$host_ipv6" ] && break
|
[ -n "$host_ipv4" ] && [ -n "$detected_host_ipv6" ] && break
|
||||||
sleep 1 && i=$((i + 1))
|
sleep 1 && i=$((i + 1))
|
||||||
done
|
done
|
||||||
#Tailscale
|
#Tailscale
|
||||||
[ "$ts_service" = ON ] && {
|
[ "$ts_service" = ON ] && ts_host_ipv4=' 100.64.0.0/10'
|
||||||
ts_host_ipv4=' 100.64.0.0/10'
|
|
||||||
ts_host_ipv6=' fd7a:115c:a1e0::/48'
|
|
||||||
}
|
|
||||||
#Wireguard
|
#Wireguard
|
||||||
[ "$wg_service" = ON ] && {
|
if [ "$wg_service" = ON ]; then
|
||||||
. "$CRASHDIR"/configs/gateway.cfg
|
. "$CRASHDIR"/configs/gateway.cfg
|
||||||
wg_host_ipv4=' $wg_ipv4'
|
wg_host_ipv4=" $wg_ipv4"
|
||||||
[ -n "$wg_ipv6" ] && wg_host_ipv6=' $wg_ipv6'
|
fi
|
||||||
}
|
|
||||||
#添加自定义ipv4局域网网段
|
#添加自定义ipv4局域网网段
|
||||||
if [ "$replace_default_host_ipv4" = "ON" ]; then
|
if [ "$replace_default_host_ipv4" = "ON" ]; then
|
||||||
host_ipv4="$cust_host_ipv4"
|
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'
|
host_ipv4='192.168.0.0/16 10.0.0.0/12 172.16.0.0/12'
|
||||||
logger "无法获取本地LAN-IPV4网段,请前往流量过滤设置界面设置自定义网段!" 31
|
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地址
|
#获取本机出口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)
|
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)
|
[ -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)
|
||||||
|
|||||||
Executable
+123
@@ -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
|
||||||
@@ -2,13 +2,22 @@
|
|||||||
# Copyright (C) Juewuy
|
# Copyright (C) Juewuy
|
||||||
|
|
||||||
RESERVED_IP=$(echo $reserve_ipv4 | sed 's/[[:space:]]\+/, /g')
|
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(){
|
add_ip6_route(){
|
||||||
#过滤保留地址及本机地址
|
#过滤保留地址及当前局域网地址
|
||||||
nft add rule inet shellcrash $1 ip6 daddr {$RESERVED_IP6} return
|
nft add rule inet shellcrash $1 ip6 daddr {$RESERVED_IP6} return
|
||||||
#仅代理本机局域网网段流量
|
nft add rule inet shellcrash $1 ip6 daddr @lan_ip6 return
|
||||||
nft add rule inet shellcrash $1 ip6 saddr != {$HOST_IP6} return
|
#OUTPUT只包含本机流量,无需依赖动态公网源前缀
|
||||||
|
[ "$1" = 'prerouting' ] && nft add rule inet shellcrash $1 ip6 saddr != @lan_ip6 return
|
||||||
#绕过CN_IPV6
|
#绕过CN_IPV6
|
||||||
[ "$dns_mod" != "fake-ip" -a "$cn_ip_route" = "ON" -a -f "$BINDIR"/cn_ipv6.txt ] && {
|
[ "$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)
|
CN_IP6=$(awk '{printf "%s, ",$1}' "$BINDIR"/cn_ipv6.txt)
|
||||||
@@ -83,10 +92,8 @@ start_nft_route() { #nftables-route通用工具
|
|||||||
}
|
}
|
||||||
#局域网ipv6支持
|
#局域网ipv6支持
|
||||||
if [ "$ipv6_redir" = "ON" -a "$1" = 'prerouting' -a "$firewall_area" != 5 ]; then
|
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"
|
add_ip6_route "$1"
|
||||||
elif [ "$ipv6_redir" = "ON" -a "$1" = 'output' -a \( "$firewall_area" = 2 -o "$firewall_area" = 3 \) ]; then
|
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"
|
add_ip6_route "$1"
|
||||||
else
|
else
|
||||||
nft add rule inet shellcrash $1 meta nfproto ipv6 return
|
nft add rule inet shellcrash $1 meta nfproto ipv6 return
|
||||||
@@ -106,7 +113,6 @@ start_nft_route() { #nftables-route通用工具
|
|||||||
start_nft_dns() { #nftables-dns
|
start_nft_dns() { #nftables-dns
|
||||||
[ "$1" = 'prerouting' ] && {
|
[ "$1" = 'prerouting' ] && {
|
||||||
HOST_IP=$(echo $host_ipv4 | sed 's/[[:space:]]\+/, /g')
|
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" = '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')"
|
[ "$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
|
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
|
[ "$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 #屏蔽外部请求
|
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 ] && {
|
[ "$1" = 'prerouting' ] && [ -s "$CRASHDIR"/configs/mac ] && {
|
||||||
MAC=$(awk '{printf "%s, ",$1}' "$CRASHDIR"/configs/mac)
|
MAC=$(awk '{printf "%s, ",$1}' "$CRASHDIR"/configs/mac)
|
||||||
@@ -134,7 +140,6 @@ start_nft_dns() { #nftables-dns
|
|||||||
}
|
}
|
||||||
start_nft_wan() { #nftables公网防火墙
|
start_nft_wan() { #nftables公网防火墙
|
||||||
HOST_IP=$(echo $host_ipv4 | sed 's/[[:space:]]\+/, /g')
|
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 chain inet shellcrash input { type filter hook input priority -100 \; }
|
||||||
nft add rule inet shellcrash input iif lo accept #本机请求全放行
|
nft add rule inet shellcrash input iif lo accept #本机请求全放行
|
||||||
#端口放行
|
#端口放行
|
||||||
@@ -148,7 +153,7 @@ start_nft_wan() { #nftables公网防火墙
|
|||||||
#端口拦截
|
#端口拦截
|
||||||
reject_ports="{ $mix_port, $db_port }"
|
reject_ports="{ $mix_port, $db_port }"
|
||||||
nft add rule inet shellcrash input ip saddr {$HOST_IP} accept
|
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 tcp dport $reject_ports reject
|
||||||
nft add rule inet shellcrash input udp dport $reject_ports reject
|
nft add rule inet shellcrash input udp dport $reject_ports reject
|
||||||
#fw4特殊处理
|
#fw4特殊处理
|
||||||
@@ -160,6 +165,7 @@ start_nftables() { #nftables配置总入口
|
|||||||
#初始化nftables
|
#初始化nftables
|
||||||
nft add table inet shellcrash 2>/dev/null
|
nft add table inet shellcrash 2>/dev/null
|
||||||
nft flush 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
|
[ "$fw_wan" != OFF ] && [ "$systype" != 'container' ] && start_nft_wan
|
||||||
#启动DNS劫持
|
#启动DNS劫持
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Copyright (C) Juewuy
|
# Copyright (C) Juewuy
|
||||||
|
|
||||||
|
#停止旧的IPv6前缀监听,避免防火墙重建期间并发更新
|
||||||
|
[ -f "$CRASHDIR"/starts/fw_ipv6_watch.sh ] && /bin/sh "$CRASHDIR"/starts/fw_ipv6_watch.sh stop
|
||||||
#获取局域网host地址
|
#获取局域网host地址
|
||||||
. "$CRASHDIR"/starts/fw_getlanip.sh && getlanip
|
. "$CRASHDIR"/starts/fw_getlanip.sh && getlanip
|
||||||
#缺省值
|
#缺省值
|
||||||
@@ -46,6 +48,9 @@
|
|||||||
#防火墙配置
|
#防火墙配置
|
||||||
[ "$firewall_mod" = 'iptables' ] && . "$CRASHDIR"/starts/fw_iptables.sh && start_iptables
|
[ "$firewall_mod" = 'iptables' ] && . "$CRASHDIR"/starts/fw_iptables.sh && start_iptables
|
||||||
[ "$firewall_mod" = 'nftables' ] && . "$CRASHDIR"/starts/fw_nftables.sh && start_nftables
|
[ "$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查询失败的问题
|
#修复部分虚拟机dns查询失败的问题
|
||||||
[ "$firewall_area" = 2 -o "$firewall_area" = 3 ] && [ -z "$(grep '127.0.0.1' /etc/resolv.conf 2>/dev/null)" ] && [ "$systype" != 'container' ] && {
|
[ "$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}')
|
line=$(grep -n 'nameserver' /etc/resolv.conf | awk -F: 'FNR==1{print $1}')
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
[ -z "$CRASHDIR" ] && CRASHDIR=$( cd $(dirname $0);cd ..;pwd)
|
[ -z "$CRASHDIR" ] && CRASHDIR=$( cd $(dirname $0);cd ..;pwd)
|
||||||
. "$CRASHDIR"/libs/get_config.sh
|
. "$CRASHDIR"/libs/get_config.sh
|
||||||
. "$CRASHDIR"/libs/check_cmd.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地址
|
. "$CRASHDIR"/starts/fw_getlanip.sh && getlanip #获取局域网host地址
|
||||||
#缺省值
|
#缺省值
|
||||||
[ -z "$common_ports" ] && common_ports='ON'
|
[ -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
|
[ -s /etc/init.d/firewall.bak ] && mv -f /etc/init.d/firewall.bak /etc/init.d/firewall
|
||||||
#others
|
#others
|
||||||
sed -i '/shellcrash-dns-repair/d' /etc/resolv.conf 2>/dev/null
|
sed -i '/shellcrash-dns-repair/d' /etc/resolv.conf 2>/dev/null
|
||||||
|
|
||||||
|
|||||||
Executable
+83
@@ -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'
|
||||||
Executable
+78
@@ -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'
|
||||||
Reference in New Issue
Block a user