mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-31 00:35:13 +08:00
🌴 Sync 2026-03-05 23:51:42
This commit is contained in:
1 parent
97c049ed0a
commit
0acff03804
14253 files changed
+3893239
No files matched your search
@@ -0,0 +1,21 @@
|
||||
|
||||
config timecontrol
|
||||
option enabled '0'
|
||||
option control_mode 'blacklist'
|
||||
option list_type 'blacklist'
|
||||
option chain 'input'
|
||||
|
||||
config device
|
||||
option timestart '00:00'
|
||||
option week '0'
|
||||
option timeend '23:55'
|
||||
option mac ''
|
||||
option enable '0'
|
||||
|
||||
config device
|
||||
option mac '192.168.10.10/24'
|
||||
option timestart '00:00'
|
||||
option timeend '00:00'
|
||||
option week '0'
|
||||
option enable '0'
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
#
|
||||
# Copyright (C) 2023 sirpdboy herboy2008@gmail.com
|
||||
#
|
||||
|
||||
START=99
|
||||
USE_PROCD=1
|
||||
|
||||
NAME=timecontrol
|
||||
LOCK="/var/lock/$NAME.lock"
|
||||
|
||||
start_instance() {
|
||||
procd_open_instance
|
||||
procd_set_param command /usr/bin/timecontrolctrl
|
||||
procd_set_param respawn
|
||||
procd_set_param stderr 1
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
_timecontrol_start() {
|
||||
if [ "$(grep -c 'option enable .1.' /etc/config/$NAME 2>/dev/null)" -gt "0" ]; then
|
||||
touch $LOCK
|
||||
timecontrol start
|
||||
start_instance
|
||||
else
|
||||
echo "INFO: ûÓÐÈκιÜÖÆµÄÉ豸£¡"
|
||||
stop_service
|
||||
fi
|
||||
}
|
||||
|
||||
start_service(){
|
||||
[ -f $LOCK ] && exit
|
||||
_timecontrol_start
|
||||
rm -f $LOCK
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger 'timecontrol'
|
||||
}
|
||||
|
||||
stop_service(){
|
||||
kill -9 $(busybox ps -w | grep 'timecontrolctrl' | grep -v 'grep' | awk '{print $1}') >/dev/null 2>&1
|
||||
rm -f $LOCK 2>/dev/null
|
||||
timecontrol stop
|
||||
}
|
||||
|
||||
|
||||
reload_service() {
|
||||
restart
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
[ ! -f "/usr/share/ucitrack/luci-app-timecontrol.json" ] && {
|
||||
cat > /usr/share/ucitrack/luci-app-timecontrol.json << EEOF
|
||||
{
|
||||
"config": "timecontrol",
|
||||
"init": "timecontrol"
|
||||
}
|
||||
EEOF
|
||||
}
|
||||
chmod +x /etc/init.d/timecontrol /usr/bin/timecontrol*
|
||||
uci -q batch <<-EOF >/dev/null
|
||||
delete ucitrack.@timecontrol[-1]
|
||||
add ucitrack timecontrol
|
||||
set ucitrack.@timecontrol[-1].init=timecontrol
|
||||
commit ucitrack
|
||||
EOF
|
||||
|
||||
[ -s /etc/config/timecontrol ] || echo "config timecontrol" > /etc/config/timecontrol
|
||||
rm -f /tmp/luci-indexcache
|
||||
exit 0
|
||||
+840
@@ -0,0 +1,840 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (C) 2006 OpenWrt.org
|
||||
# Copyright 2022-2025 sirpdboy <herboy2008@gmail.com>
|
||||
|
||||
crrun=$1
|
||||
crid=$2
|
||||
NAME=timecontrol
|
||||
DEBUG=0
|
||||
|
||||
config_get_type() {
|
||||
local ret=$(uci -q get "${NAME}.${1}")
|
||||
echo "${ret:=$2}"
|
||||
}
|
||||
|
||||
config_n_get() {
|
||||
local ret=$(uci -q get "${NAME}.${1}.${2}")
|
||||
echo "${ret:=$3}"
|
||||
}
|
||||
|
||||
config_t_get() {
|
||||
local index=${3:-0}
|
||||
local default=$4
|
||||
local ret=$(uci -q get "${NAME}.@${1}[${index}].${2}")
|
||||
echo "${ret:-$default}"
|
||||
}
|
||||
|
||||
config_t_set() {
|
||||
local index=${3:-0}
|
||||
local ret=$(uci -q set "${NAME}.@${1}[${index}].${2}=${3}")
|
||||
}
|
||||
|
||||
IDLIST="/var/$NAME.idlist"
|
||||
TMPID="/var/$NAME.tmpid"
|
||||
LOG_FILE="/var/log/$NAME.log"
|
||||
list_type=$(config_t_get $NAME list_type 0 )
|
||||
|
||||
# 获取控制强度配置
|
||||
CHAIN=$(config_t_get $NAME chain 0 )
|
||||
|
||||
|
||||
bin_nft=$(which nft 2>/dev/null)
|
||||
bin_iptables=$(which iptables 2>/dev/null)
|
||||
bin_ip6tables=$(which ip6tables 2>/dev/null)
|
||||
bin_conntrack=$(which conntrack 2>/dev/null)
|
||||
|
||||
is_input_chain() {
|
||||
[ "$CHAIN" = "input" ]
|
||||
}
|
||||
|
||||
dbg() {
|
||||
if [ "$DEBUG" -eq 1 ] ;then
|
||||
local d="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
echo "[$d] DEBUG: $@ " >>$LOG_FILE
|
||||
echo "DEBUG: $@" # 直接输出到终端以便调试
|
||||
fi
|
||||
}
|
||||
|
||||
info() {
|
||||
local d="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
echo "[$d] INFO: $@" >>$LOG_FILE
|
||||
echo "INFO: $@"
|
||||
}
|
||||
|
||||
parse_target() {
|
||||
local target="$1"
|
||||
|
||||
# 去除空格
|
||||
target=$(echo "${target}" | xargs)
|
||||
|
||||
# 1. 单个IP地址 (192.168.1.100)
|
||||
if echo "$target" | grep -qE '^([0-9]{1,3}\.){3}[0-9]{1,3}$'; then
|
||||
local octets=(${target//./ })
|
||||
for octet in "${octets[@]}"; do
|
||||
[ "$octet" -le 255 ] || return 1
|
||||
done
|
||||
echo "ipv4:single:${target}"
|
||||
return 0
|
||||
|
||||
# 2. IP范围 (192.168.1.1-192.168.1.100)
|
||||
elif echo "$target" | grep -qE '^([0-9]{1,3}\.){3}[0-9]{1,3}-([0-9]{1,3}\.){3}[0-9]{1,3}$'; then
|
||||
local start_ip=${target%-*}
|
||||
local end_ip=${target#*-}
|
||||
|
||||
# 验证起始IP和结束IP
|
||||
local start_octets=(${start_ip//./ })
|
||||
local end_octets=(${end_ip//./ })
|
||||
for i in {0..3}; do
|
||||
[ "${start_octets[$i]}" -le 255 ] && [ "${end_octets[$i]}" -le 255 ] || return 1
|
||||
done
|
||||
|
||||
echo "ipv4:range:${target}"
|
||||
return 0
|
||||
|
||||
# 3. CIDR格式 (192.168.1.0/24)
|
||||
elif echo "$target" | grep -qE '^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$'; then
|
||||
local ip=${target%/*}
|
||||
local mask=${target#*/}
|
||||
|
||||
# 验证掩码
|
||||
if [ "$mask" -ge 0 ] && [ "$mask" -le 32 ]; then
|
||||
local octets=(${ip//./ })
|
||||
for octet in "${octets[@]}"; do
|
||||
[ "$octet" -le 255 ] || return 1
|
||||
done
|
||||
echo "ipv4:cidr:${target}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 4. MAC地址 (00:11:22:33:44:55)
|
||||
elif echo "$target" | grep -qE '^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$'; then
|
||||
echo "mac:single:${target,,}" # 转换为小写
|
||||
return 0
|
||||
|
||||
# 5. IPv6地址(简单验证)
|
||||
elif echo "$target" | grep -qE '^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$'; then
|
||||
echo "ipv6:single:${target}"
|
||||
return 0
|
||||
|
||||
# 6. IPv6 CIDR
|
||||
elif echo "$target" | grep -qE '^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}/[0-9]{1,3}$'; then
|
||||
local ipv6=${target%/*}
|
||||
local mask=${target#*/}
|
||||
if [ "$mask" -ge 0 ] && [ "$mask" -le 128 ]; then
|
||||
echo "ipv6:cidr:${target}"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
convert_to_old_format() {
|
||||
local target="$1"
|
||||
local parsed_result=$(parse_target "$target")
|
||||
[ $? -ne 0 ] && return 1
|
||||
|
||||
IFS=':' read -r type subtype value <<< "$parsed_result"
|
||||
|
||||
case "$type" in
|
||||
"ipv4")
|
||||
case "$subtype" in
|
||||
"single")
|
||||
echo "ip ipv4_addr $value"
|
||||
;;
|
||||
"range")
|
||||
# 转换为nftables范围格式
|
||||
local start_ip=${value%-*}
|
||||
local end_ip=${value#*-}
|
||||
echo "ip ipv4_addr { $start_ip-$end_ip }"
|
||||
;;
|
||||
"cidr")
|
||||
echo "ip ipv4_addr $value"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
"mac")
|
||||
echo "bridge ether_addr $value"
|
||||
;;
|
||||
"ipv6")
|
||||
case "$subtype" in
|
||||
"single") echo "ip6 ipv6_addr $value" ;;
|
||||
"cidr") echo "ip6 ipv6_addr $value" ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
flush_connections() {
|
||||
local target="$1"
|
||||
|
||||
if [ -x "$bin_conntrack" ]; then
|
||||
local parsed_result=$(parse_target "${target}")
|
||||
if [ $? -eq 0 ]; then
|
||||
IFS=':' read -r type subtype value <<< "${parsed_result}"
|
||||
|
||||
case "${type}" in
|
||||
"ipv4")
|
||||
case "$subtype" in
|
||||
"single")
|
||||
$bin_conntrack -D -s "${value}" 2>/dev/null || true
|
||||
$bin_conntrack -D -d "${value}" 2>/dev/null || true
|
||||
dbg "清理单个IP连接: ${value}"
|
||||
;;
|
||||
"range")
|
||||
# 对于IP范围,需要转换为CIDR或逐个清理
|
||||
# 这里简化处理:提取起始IP的第一个八位组
|
||||
local start_ip=${value%-*}
|
||||
local network="${start_ip%.*}.0/24" # 假设为/24网络
|
||||
$bin_conntrack -D -s "${network}" 2>/dev/null || true
|
||||
dbg "清理IP范围连接: ${value}"
|
||||
;;
|
||||
"cidr")
|
||||
$bin_conntrack -D -s "${value}" 2>/dev/null || true
|
||||
$bin_conntrack -D -d "${value}" 2>/dev/null || true
|
||||
dbg "清理CIDR连接: ${value}"
|
||||
;;
|
||||
esac
|
||||
info "断开连接: ${target}"
|
||||
;;
|
||||
"mac")
|
||||
# 从ARP表查找IP
|
||||
if [ -f "/proc/net/arp" ]; then
|
||||
local ip_addr=$(grep -i "${value}" /proc/net/arp 2>/dev/null | awk '{print $1}' | head -1)
|
||||
if [ -n "${ip_addr}" ]; then
|
||||
$bin_conntrack -D -s "${ip_addr}" 2>/dev/null || true
|
||||
$bin_conntrack -D -d "${ip_addr}" 2>/dev/null || true
|
||||
dbg "清理MAC连接: ${value} (IP: ${ip_addr})"
|
||||
info "断开MAC连接: ${value}"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
"ipv6")
|
||||
dbg "IPv6连接清理暂不支持: ${value}"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
dbg "无法解析目标地址: ${target}"
|
||||
fi
|
||||
else
|
||||
dbg "conntrack未安装,跳过连接清理"
|
||||
fi
|
||||
}
|
||||
|
||||
send_tcp_reset() {
|
||||
local target="$1"
|
||||
|
||||
[ "$TCP_RST" != "1" ] && return
|
||||
|
||||
local parsed_result=$(parse_target "${target}")
|
||||
[ $? -ne 0 ] && return
|
||||
|
||||
IFS=':' read -r type subtype value <<< "${parsed_result}"
|
||||
[ "$type" != "ipv4" ] && return
|
||||
|
||||
info "发送TCP RST包: ${target}"
|
||||
|
||||
# ============ iptables ============
|
||||
if [ -n "$iptables_ver" ]; then
|
||||
case "$subtype" in
|
||||
"single"|"cidr")
|
||||
iptables -I FORWARD -s "$value" -p tcp -j REJECT --reject-with tcp-reset 2>/dev/null || true
|
||||
[ "$StrongCHAIN" = "1" ] && iptables -I INPUT -s "$value" -p tcp -j REJECT --reject-with tcp-reset 2>/dev/null || true
|
||||
;;
|
||||
"range")
|
||||
local start_ip=${value%-*}
|
||||
local end_ip=${value#*-}
|
||||
iptables -I FORWARD -m iprange --src-range "$start_ip-$end_ip" -p tcp -j REJECT --reject-with tcp-reset 2>/dev/null || true
|
||||
[ "$StrongCHAIN" = "1" ] && iptables -I INPUT -m iprange --src-range "$start_ip-$end_ip" -p tcp -j REJECT --reject-with tcp-reset 2>/dev/null || true
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# ============ nftables ============
|
||||
if [ -n "$nftables_ver" ]; then
|
||||
case "$subtype" in
|
||||
"single"|"cidr")
|
||||
nft add rule inet timecontrol forward ip saddr "$value" tcp flags rst counter reject with tcp reset 2>/dev/null || true
|
||||
[ "$StrongCHAIN" = "1" ] && nft add rule inet timecontrol input ip saddr "$value" tcp flags rst counter reject with tcp reset 2>/dev/null || true
|
||||
;;
|
||||
"range")
|
||||
local start_ip=${value%-*}
|
||||
local end_ip=${value#*-}
|
||||
nft add rule inet timecontrol forward ip saddr >= "$start_ip" ip saddr <= "$end_ip" tcp flags rst counter reject with tcp reset 2>/dev/null || true
|
||||
[ "$StrongCHAIN" = "1" ] && nft add rule inet timecontrol input ip saddr >= "$start_ip" ip saddr <= "$end_ip" tcp flags rst counter reject with tcp reset 2>/dev/null || true
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# 10秒后自动删除
|
||||
(sleep 10; remove_tcp_reset "$target") &
|
||||
}
|
||||
|
||||
remove_tcp_reset() {
|
||||
local target="$1"
|
||||
local parsed_result=$(parse_target "$target")
|
||||
[ $? -ne 0 ] && return
|
||||
|
||||
IFS=':' read -r type subtype value <<< "$parsed_result"
|
||||
[ "$type" != "ipv4" ] && return
|
||||
|
||||
# iptables删除
|
||||
if [ -n "$iptables_ver" ]; then
|
||||
case "$subtype" in
|
||||
"single"|"cidr")
|
||||
iptables -D FORWARD -s "$value" -p tcp -j REJECT --reject-with tcp-reset 2>/dev/null || true
|
||||
[ "$StrongCHAIN" = "1" ] && iptables -D INPUT -s "$value" -p tcp -j REJECT --reject-with tcp-reset 2>/dev/null || true
|
||||
;;
|
||||
"range")
|
||||
local start_ip=${value%-*}
|
||||
local end_ip=${value#*-}
|
||||
iptables -D FORWARD -m iprange --src-range "$start_ip-$end_ip" -p tcp -j REJECT --reject-with tcp-reset 2>/dev/null || true
|
||||
[ "$StrongCHAIN" = "1" ] && iptables -D INPUT -m iprange --src-range "$start_ip-$end_ip" -p tcp -j REJECT --reject-with tcp-reset 2>/dev/null || true
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# nftables删除(简化:重新加载表)
|
||||
if [ -n "$nftables_ver" ]; then
|
||||
# 简单的做法是重新创建表
|
||||
nft delete table inet timecontrol 2>/dev/null || true
|
||||
# 重新初始化
|
||||
init_timecontrol
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Check if nftables/iptables is available
|
||||
if [ -x "$bin_nft" ]; then
|
||||
nftables_ver="true"
|
||||
dbg "nft found: $bin_nft"
|
||||
elif [ -x "$bin_iptables" ] || [ -x "$bin_ip6tables" ]; then
|
||||
iptables_ver="true"
|
||||
dbg "iptables found: $bin_iptables"
|
||||
else
|
||||
dbg "No firewall tool found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
nft() {
|
||||
dbg "nft $*"
|
||||
$bin_nft "$@" 2>/dev/null
|
||||
}
|
||||
|
||||
iptables() {
|
||||
dbg "iptables $*"
|
||||
$bin_iptables "$@" 2>/dev/null
|
||||
}
|
||||
|
||||
ip6tables() {
|
||||
dbg "ip6tables $*"
|
||||
$bin_ip6tables "$@" 2>/dev/null
|
||||
}
|
||||
|
||||
get_target_info() {
|
||||
local target="$1"
|
||||
local result=$(convert_to_old_format "$target")
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "$result"
|
||||
return 0
|
||||
fi
|
||||
target=$(echo "${target}" | xargs)
|
||||
if echo "$target" | grep -qE '^([0-9]{1,3}\.){3}[0-9]{1,3}$'; then
|
||||
local octets=(${target//./ })
|
||||
for octet in "${octets[@]}"; do
|
||||
[ "$octet" -le 255 ] || return 1
|
||||
done
|
||||
table="ip"
|
||||
addr_type="ipv4_addr"
|
||||
elif echo "$target" | grep -qE '^([0-9]{1,3}\.){3}[0-9]{1,3}-([0-9]{1,3}\.){3}[0-9]{1,3}$'; then
|
||||
local start_ip=${target%-*}
|
||||
local end_ip=${target#*-}
|
||||
table="ip"
|
||||
addr_type="ipv4_addr"
|
||||
target="{ $start_ip-$end_ip }"
|
||||
elif echo "$target" | grep -qE '^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$'; then
|
||||
local ip=${target%/*}
|
||||
local mask=${target#*/}
|
||||
[ "$mask" -le 32 ] || return 1
|
||||
table="ip"
|
||||
addr_type="ipv4_addr"
|
||||
elif echo "$target" | grep -qE '^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$'; then
|
||||
table="bridge"
|
||||
addr_type="ether_addr"
|
||||
target=$(echo "$target" | tr '[:upper:]' '[:lower:]')
|
||||
elif echo "$target" | grep -qE '^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$'; then
|
||||
table="ip6"
|
||||
addr_type="ipv6_addr"
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$table $addr_type $target"
|
||||
}
|
||||
|
||||
nft_table_exists() {
|
||||
local table=$1
|
||||
nft list tables | grep -q "$table"
|
||||
}
|
||||
|
||||
nft_set_exists() {
|
||||
local table=$1
|
||||
local set=$2
|
||||
nft list sets | grep -q "$table $set"
|
||||
}
|
||||
|
||||
nft_rule_exists() {
|
||||
local table=$1
|
||||
local chain=$2
|
||||
local rule=$3
|
||||
nft list chain "$table" "$chain" | grep -q "$rule"
|
||||
}
|
||||
|
||||
stop_timecontrol() {
|
||||
remove_tcp_reset
|
||||
if [ -n "$nftables_ver" ]; then
|
||||
for chain in "ip" "bridge" "ip6"; do
|
||||
if nft_table_exists "$chain filter"; then
|
||||
if [ "$StrongCHAIN" -eq 1 ]; then
|
||||
nft flush chain "$chain" filter input
|
||||
nft delete chain "$chain" filter input 2>/dev/null
|
||||
fi
|
||||
nft flush chain "$chain" filter forward
|
||||
nft delete chain "$chain" filter forward 2>/dev/null
|
||||
|
||||
if nft_set_exists "$chain filter" "${list_type}_list"; then
|
||||
nft delete set "$chain" filter "${list_type}_list"
|
||||
fi
|
||||
nft delete table "$chain" filter
|
||||
fi
|
||||
done
|
||||
dbg "All nftables rules have been cleared."
|
||||
elif [ -n "$iptables_ver" ]; then
|
||||
|
||||
|
||||
if [ "$StrongCHAIN" -eq 1 ]; then
|
||||
iptables -D INPUT -m set --match-set timecontrol_blacklist src -j DROP 2>/dev/null
|
||||
ip6tables -D INPUT -m set --match-set timecontrol_blacklistv6 src -j DROP 2>/dev/null
|
||||
fi
|
||||
|
||||
iptables -D FORWARD -m set --match-set timecontrol_blacklist src -j DROP 2>/dev/null
|
||||
ip6tables -D FORWARD -m set --match-set timecontrol_blacklistv6 src -j DROP 2>/dev/null
|
||||
|
||||
ipset flush timecontrol_blacklist 2>/dev/null
|
||||
ipset flush timecontrol_blacklistv6 2>/dev/null
|
||||
ipset destroy timecontrol_blacklist 2>/dev/null
|
||||
ipset destroy timecontrol_blacklistv6 2>/dev/null
|
||||
dbg "Deleted iptables rules and ipsets"
|
||||
fi
|
||||
|
||||
rm -rf "$IDLIST" "$TMPID"
|
||||
}
|
||||
|
||||
init_timecontrol() {
|
||||
info "初始化时间控制"
|
||||
|
||||
if [ -n "$nftables_ver" ]; then
|
||||
for chain in "ip" "bridge" "ip6"; do
|
||||
case $chain in
|
||||
ip)
|
||||
addr_type="ipv4_addr"
|
||||
ip_protocol="ip"
|
||||
;;
|
||||
ip6)
|
||||
addr_type="ipv6_addr"
|
||||
ip_protocol="ip6"
|
||||
;;
|
||||
bridge)
|
||||
addr_type="ether_addr"
|
||||
ip_protocol="ether"
|
||||
;;
|
||||
esac
|
||||
|
||||
if ! nft_table_exists "$chain filter"; then
|
||||
nft add table "$chain" filter
|
||||
dbg "Created table $chain filter"
|
||||
fi
|
||||
|
||||
if ! nft_set_exists "$chain filter" "${list_type}_list"; then
|
||||
nft add set "$chain" filter "${list_type}_list" "{ type $addr_type; flags interval; }"
|
||||
dbg "Created set ${list_type}_list in $chain filter"
|
||||
fi
|
||||
|
||||
if [ "$StrongCHAIN" -eq 1 ]; then
|
||||
if ! nft list chain "$chain" filter input 2>/dev/null; then
|
||||
nft add chain "$chain" filter input "{ type filter hook input priority -100; }"
|
||||
dbg "Created INPUT chain in $chain filter (强控制)"
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! nft list chain "$chain" filter forward 2>/dev/null; then
|
||||
nft add chain "$chain" filter forward "{ type filter hook forward priority -100; }"
|
||||
dbg "Created FORWARD chain in $chain filter"
|
||||
fi
|
||||
done
|
||||
elif [ -n "$iptables_ver" ]; then
|
||||
ipset create timecontrol_blacklist hash:net 2>/dev/null || ipset flush timecontrol_blacklist
|
||||
ipset create timecontrol_blacklistv6 hash:net family inet6 2>/dev/null || ipset flush timecontrol_blacklistv6
|
||||
|
||||
dbg "Created ipsets: timecontrol_blacklist and timecontrol_blacklistv6"
|
||||
|
||||
if [ "$StrongCHAIN" -eq 1 ]; then
|
||||
if ! iptables -C INPUT -m set --match-set timecontrol_blacklist src -j DROP 2>/dev/null; then
|
||||
iptables -I INPUT -m set --match-set timecontrol_blacklist src -j DROP
|
||||
dbg "Added INPUT chain rule for IPv4 (强控制)"
|
||||
fi
|
||||
|
||||
if ! ip6tables -C INPUT -m set --match-set timecontrol_blacklistv6 src -j DROP 2>/dev/null; then
|
||||
ip6tables -I INPUT -m set --match-set timecontrol_blacklistv6 src -j DROP
|
||||
dbg "Added INPUT chain rule for IPv6 (强控制)"
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! iptables -C FORWARD -m set --match-set timecontrol_blacklist src -j DROP 2>/dev/null; then
|
||||
iptables -I FORWARD -m set --match-set timecontrol_blacklist src -j DROP
|
||||
dbg "Added FORWARD chain rule for IPv4"
|
||||
fi
|
||||
|
||||
if ! ip6tables -C FORWARD -m set --match-set timecontrol_blacklistv6 src -j DROP 2>/dev/null; then
|
||||
ip6tables -I FORWARD -m set --match-set timecontrol_blacklistv6 src -j DROP
|
||||
dbg "Added FORWARD chain rule for IPv6"
|
||||
fi
|
||||
fi
|
||||
|
||||
info "初始化完成"
|
||||
}
|
||||
|
||||
|
||||
timeadd() {
|
||||
local id=$1
|
||||
local target=$(config_t_get device mac "$id")
|
||||
[ -z "$target" ] && return
|
||||
|
||||
info "添加设备控制: $target"
|
||||
|
||||
local parsed_result=$(parse_target "$target")
|
||||
if [ $? -ne 0 ]; then
|
||||
dbg "无法解析目标地址: $target"
|
||||
return
|
||||
fi
|
||||
|
||||
local target_info=$(convert_to_old_format "$target")
|
||||
if [ $? -ne 0 ]; then
|
||||
dbg "无法转换目标格式: $target"
|
||||
return
|
||||
fi
|
||||
|
||||
read -r table addr_type target_val <<< "$target_info"
|
||||
|
||||
case $table in
|
||||
ip)
|
||||
saddr="ip saddr"
|
||||
daddr="ip daddr"
|
||||
ipset_name="timecontrol_blacklist"
|
||||
;;
|
||||
ip6)
|
||||
saddr="ip6 saddr"
|
||||
daddr="ip6 daddr"
|
||||
ipset_name="timecontrol_blacklistv6"
|
||||
;;
|
||||
bridge)
|
||||
saddr="ether saddr"
|
||||
daddr="ether daddr"
|
||||
ipset_name="timecontrol_blacklistbridge"
|
||||
;;
|
||||
*)
|
||||
dbg "未知的table类型: $table"
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -n "$nftables_ver" ]; then
|
||||
nft add element "$table" filter "${list_type}_list" "{ $target_val }"
|
||||
|
||||
if [ "$StrongCHAIN" -eq 1 ]; then
|
||||
if ! nft list chain "$table" filter input 2>/dev/null | grep -q "@${list_type}_list"; then
|
||||
nft add rule "$table" filter input "$saddr" @"${list_type}_list" drop
|
||||
dbg "Added INPUT rule for $target in table $table (强控制)"
|
||||
fi
|
||||
fi
|
||||
if ! nft list chain "$table" filter forward 2>/dev/null | grep -q "@${list_type}_list"; then
|
||||
nft add rule "$table" filter forward "$saddr" @"${list_type}_list" drop
|
||||
dbg "Added FORWARD rule for $target in table $table"
|
||||
fi
|
||||
|
||||
elif [ -n "$iptables_ver" ]; then
|
||||
# 对于iptables,需要根据不同类型处理
|
||||
IFS=':' read -r type subtype value <<< "$parsed_result"
|
||||
|
||||
case "$type" in
|
||||
"ipv4")
|
||||
case "$subtype" in
|
||||
"single"|"cidr")
|
||||
ipset add "$ipset_name" "$value" 2>/dev/null || true
|
||||
;;
|
||||
"range")
|
||||
# IP范围需要特殊处理
|
||||
local start_ip=${value%-*}
|
||||
local end_ip=${value#*-}
|
||||
# 将范围转换为多个CIDR或使用iprange模块
|
||||
ipset add "$ipset_name" "$start_ip-$end_ip" 2>/dev/null || true
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
"mac")
|
||||
ipset add "$ipset_name" "$value" 2>/dev/null || true
|
||||
;;
|
||||
esac
|
||||
dbg "Added $target to ipset $ipset_name"
|
||||
fi
|
||||
if [ "$StrongCHAIN" -eq 1 ]; then
|
||||
flush_connections "$target"
|
||||
send_tcp_reset "$target"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
timedel() {
|
||||
local id=$1
|
||||
local target=$(config_t_get device mac "$id")
|
||||
[ -z "$target" ] && return
|
||||
|
||||
info "移除设备控制: $target"
|
||||
|
||||
# 解析目标
|
||||
local parsed_result=$(parse_target "$target")
|
||||
if [ $? -ne 0 ]; then
|
||||
dbg "无法解析目标地址: $target"
|
||||
return
|
||||
fi
|
||||
|
||||
local target_info=$(convert_to_old_format "$target")
|
||||
if [ $? -ne 0 ]; then
|
||||
dbg "无法转换目标格式: $target"
|
||||
return
|
||||
fi
|
||||
|
||||
read -r table addr_type target_val <<< "$target_info"
|
||||
|
||||
case $table in
|
||||
ip) ipset_name="timecontrol_blacklist" ;;
|
||||
ip6) ipset_name="timecontrol_blacklistv6" ;;
|
||||
bridge) ipset_name="timecontrol_blacklistbridge" ;;
|
||||
*) return ;;
|
||||
esac
|
||||
|
||||
if [ -n "$nftables_ver" ]; then
|
||||
nft delete element "$table" filter "${list_type}_list" "{ $target_val }" 2>/dev/null
|
||||
dbg "Removed $target_val from ${list_type}_list in table $table"
|
||||
else
|
||||
IFS=':' read -r type subtype value <<< "$parsed_result"
|
||||
ipset del "$ipset_name" "$value" 2>/dev/null || true
|
||||
dbg "Removed $target from ipset $ipset_name"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
check_time() {
|
||||
local start=$1
|
||||
local end=$2
|
||||
local current=$(date +%H%M)
|
||||
local start_min=$((10#${start:0:2}*60 + 10#${start:3:2}))
|
||||
local end_min=$((10#${end:0:2}*60 + 10#${end:3:2}))
|
||||
local current_min=$((10#${current:0:2}*60 + 10#${current:2:2}))
|
||||
|
||||
if [[ $start_min -lt $end_min ]]; then
|
||||
[[ $current_min -ge $start_min && $current_min -lt $end_min ]]
|
||||
else
|
||||
[[ $current_min -ge $start_min || $current_min -lt $end_min ]]
|
||||
fi
|
||||
}
|
||||
|
||||
check_list() {
|
||||
local i=$1
|
||||
local start_time=$(config_t_get device timestart "$i")
|
||||
local end_time=$(config_t_get device timeend "$i")
|
||||
local wweek=$(config_t_get device week "$i")
|
||||
local current_weekday=$(date +%u)
|
||||
|
||||
# 检查时间
|
||||
check_time "$start_time" "$end_time" || return 1
|
||||
|
||||
# 检查星期
|
||||
if [ "$wweek" != "0" ]; then
|
||||
echo "$wweek" | grep -qw "$current_weekday" || return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
test_parse() {
|
||||
echo "=== 测试地址解析 ==="
|
||||
|
||||
local test_cases=(
|
||||
"192.168.1.100"
|
||||
"192.168.1.1-192.168.1.100"
|
||||
"192.168.1.0/24"
|
||||
"192.168.10.1-192.168.10.254"
|
||||
"192.168.10.0/24"
|
||||
"00:11:22:33:44:55"
|
||||
"2001:db8::1"
|
||||
"invalid_address"
|
||||
)
|
||||
|
||||
for test_case in "${test_cases[@]}"; do
|
||||
echo -n "测试 '$test_case': "
|
||||
if parse_target "$test_case" >/dev/null; then
|
||||
echo "✓ 通过"
|
||||
parse_target "$test_case"
|
||||
else
|
||||
echo "✗ 失败"
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
show_status() {
|
||||
echo ""
|
||||
echo "┌────────────────────────────────────────────┐"
|
||||
echo "│ 时间控制系统状态 │"
|
||||
echo "├────────────────────────────────────────────┤"
|
||||
|
||||
# 控制模式
|
||||
if [ "$StrongCHAIN" -eq 1 ]; then
|
||||
echo "│ 控制模式: 强力控制 (INPUT链) │"
|
||||
echo "│ 阻止访问路由器和互联网,立即生效 │"
|
||||
else
|
||||
echo "│ 控制模式: 普通控制 (FORWARD链) │"
|
||||
echo "│ 仅阻止互联网访问,可能有延迟 │"
|
||||
fi
|
||||
|
||||
echo "├────────────────────────────────────────────┤"
|
||||
|
||||
# 当前时间
|
||||
echo "│ 当前时间: $(date '+%Y-%m-%d %H:%M:%S') │"
|
||||
echo "│ 星期: $(date '+%u' | sed 's/1/星期一/;s/2/星期二/;s/3/星期三/;s/4/星期四/;s/5/星期五/;s/6/星期六/;s/7/星期日/') │"
|
||||
echo "├────────────────────────────────────────────┤"
|
||||
|
||||
if [ -s "$IDLIST" ]; then
|
||||
local device_count=$(wc -l < $IDLIST 2>/dev/null || echo "0")
|
||||
echo "│ 控制设备数: $device_count 个 │"
|
||||
|
||||
# 显示前5个设备
|
||||
echo "├────────────────────────────────────────────┤"
|
||||
echo "│ 控制中的设备: │"
|
||||
local count=0
|
||||
cat "$IDLIST" | sed 's/!//g' | head -5 | while read id; do
|
||||
count=$((count + 1))
|
||||
local target=$(config_t_get device mac "$id")
|
||||
local comment=$(config_t_get device comment "$id" "设备$id")
|
||||
printf "│ %2d %-15s %-20s │\n" $count "$comment" "$target"
|
||||
done
|
||||
|
||||
if [ $device_count -gt 5 ]; then
|
||||
echo "│ ... 还有 $(($device_count - 5)) 个设备 │"
|
||||
fi
|
||||
else
|
||||
|
||||
echo "│ 控制设备数: 0 个 │"
|
||||
fi
|
||||
|
||||
echo "└────────────────────────────────────────────┘"
|
||||
echo ""
|
||||
}
|
||||
|
||||
show_help() {
|
||||
echo "上网时间控制系统"
|
||||
echo ""
|
||||
echo "支持的地址格式:"
|
||||
echo " 1. 单个IP: 192.168.1.100"
|
||||
echo " 2. IP范围: 192.168.1.1-192.168.1.100"
|
||||
echo " 3. CIDR: 192.168.1.0/24"
|
||||
echo " 4. MAC地址: 00:11:22:33:44:55"
|
||||
echo ""
|
||||
echo "强力控制新增功能:"
|
||||
echo " - TCP RST包: 立即断开现有TCP连接"
|
||||
echo " - 连接清理: 清除conntrack表中的连接"
|
||||
echo ""
|
||||
echo "用法: $0 {start|stop|add <id>|del <id>|status|test|help}"
|
||||
echo ""
|
||||
echo "配置: 编辑 /etc/config/timecontrol"
|
||||
echo "日志: 查看 /var/log/timecontrol.log"
|
||||
}
|
||||
|
||||
if is_input_chain; then
|
||||
StrongCHAIN=1
|
||||
else
|
||||
StrongCHAIN=0
|
||||
fi
|
||||
|
||||
case "$crrun" in
|
||||
"stop")
|
||||
info "停止时间控制服务"
|
||||
stop_timecontrol
|
||||
info "所有防火墙规则已清除"
|
||||
;;
|
||||
"start")
|
||||
stop_timecontrol
|
||||
echo -e "\n====== 启动时间控制 ======" > /var/log/timecontrol.log
|
||||
touch $IDLIST
|
||||
idlist=$(uci show $NAME | grep "enable='1'" | grep "device" | grep -oE '\[.*?\]' | grep -o '[0-9]' | sed -e 's/^/!/g' -e 's/$/!/g' > "$IDLIST"; cat "$IDLIST" | sed -e 's/!//g')
|
||||
init_timecontrol
|
||||
for list in $(echo "$idlist" | sed -e 's/!//g'); do
|
||||
if check_list "$list"; then
|
||||
timeadd "$list"
|
||||
info "设备 $list 已添加到控制列表"
|
||||
else
|
||||
if grep -q "!${list}!" "$IDLIST"; then
|
||||
timedel "$list"
|
||||
sed -i "/!$list!/d" "$IDLIST"
|
||||
info "设备 $list 已从控制列表移除"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
info "时间控制启动完成,控制 $(wc -l < $IDLIST) 个设备"
|
||||
show_status
|
||||
;;
|
||||
"add")
|
||||
info "添加设备控制"
|
||||
for list in $(echo "$crid" | sed -e 's/!//g' | sed 's/,/ /g'); do
|
||||
if check_list "$list"; then
|
||||
timeadd "$list"
|
||||
if ! grep -q "!$list!" "$IDLIST"; then
|
||||
echo "!$list!" >> "$IDLIST"
|
||||
fi
|
||||
info "设备 $list 已添加"
|
||||
else
|
||||
if grep -q "!${list}!" "$IDLIST"; then
|
||||
timedel "$list"
|
||||
sed -i "/!$list!/d" "$IDLIST"
|
||||
info "设备 $list 已移除"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
;;
|
||||
"del")
|
||||
info "移除设备控制"
|
||||
for list in $(echo "$crid" | sed -e 's/!//g' | sed 's/,/ /g'); do
|
||||
timedel "$list"
|
||||
sed -i "/!$list!/d" "$IDLIST"
|
||||
info "设备 $list 已删除"
|
||||
done
|
||||
;;
|
||||
"status")
|
||||
show_status
|
||||
;;
|
||||
"test")
|
||||
test_parse
|
||||
;;
|
||||
"help"|"")
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
dbg "Invalid command: $crrun"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (C) 2006 OpenWrt.org
|
||||
# Copyright 2022-2025 sirpdboy <herboy2008@gmail.com>
|
||||
NAME=timecontrol
|
||||
IDLIST="/var/$NAME.idlist"
|
||||
TMPID="/var/$NAME.tmpid"
|
||||
|
||||
idlist=$(uci show $NAME | grep "enable='1'" | grep "device" | grep -oE '\[.*?\]' | grep -o '[0-9]')
|
||||
|
||||
config_get_type() {
|
||||
local ret=$(uci -q get "${NAME}.${1}")
|
||||
echo "${ret:=$2}"
|
||||
}
|
||||
|
||||
config_n_get() {
|
||||
local ret=$(uci -q get "${NAME}.${1}.${2}")
|
||||
echo "${ret:=$3}"
|
||||
}
|
||||
|
||||
config_t_get() {
|
||||
local index=${3:-0}
|
||||
local ret=$(uci -q get "${NAME}.@${1}[${index}].${2}")
|
||||
echo "${ret:=${4}}"
|
||||
}
|
||||
|
||||
config_t_set() {
|
||||
local index=${4:-0}
|
||||
local ret=$(uci -q set "${NAME}.@${1}[${index}].${2}=${3}")
|
||||
}
|
||||
|
||||
is_time_in_range() {
|
||||
local start_time=$1
|
||||
local end_time=$2
|
||||
local current_time=$(date +%H:%M)
|
||||
|
||||
if [ "$start_time" = "$end_time" ]; then
|
||||
return 0
|
||||
elif [ "$start_time" \< "$end_time" ]; then
|
||||
[ "$current_time" \> "$start_time" ] && [ "$current_time" \< "$end_time" ] && return 0
|
||||
else
|
||||
[ "$current_time" \> "$start_time" ] || [ "$current_time" \< "$end_time" ] && return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
is_weekday_in_range() {
|
||||
local configured_weekdays=$1
|
||||
local current_weekday=$(date +%u)
|
||||
|
||||
for ww in $(echo $configured_weekdays | sed 's/,/ /g'); do
|
||||
if [ "$current_weekday" = "$ww" ] || [ "$ww" = "0" ]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
check_device() {
|
||||
local id=$1
|
||||
local start_time=$(config_t_get device timestart $id )
|
||||
local end_time=$(config_t_get device timeend $id )
|
||||
local weekdays=$(config_t_get device week $id )
|
||||
|
||||
if is_weekday_in_range "$weekdays" && is_time_in_range "$start_time" "$end_time"; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
update_device_status() {
|
||||
local id=$1
|
||||
local action=$2
|
||||
|
||||
if [ "$action" = "add" ]; then
|
||||
timecontrol add $id
|
||||
echo "!${id}!" >> $IDLIST
|
||||
elif [ "$action" = "del" ]; then
|
||||
timecontrol del $id
|
||||
sed -i "/!$id!/d" $IDLIST >/dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
process_devices() {
|
||||
[ -s $IDLIST ] || touch $IDLIST
|
||||
|
||||
for id in $idlist; do
|
||||
if check_device $id; then
|
||||
if ! grep -q "!${id}!" $IDLIST; then
|
||||
update_device_status $id "add"
|
||||
fi
|
||||
else
|
||||
if grep -q "!${id}!" $IDLIST; then
|
||||
update_device_status $id "del"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
cat $IDLIST | sort | uniq > $TMPID
|
||||
mv $TMPID $IDLIST
|
||||
}
|
||||
|
||||
while :; do
|
||||
process_devices
|
||||
sleep 60
|
||||
done
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (C) 2025 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-timecontrol
|
||||
#
|
||||
|
||||
logfile="/var/log/timecontrol.log"
|
||||
lang=$(uci get luci.main.lang 2>/dev/null)
|
||||
if [ -z "$lang" ] || [[ "$lang" == "auto" ]]; then
|
||||
lang=$(echo "${LANG:-${LANGUAGE:-${LC_ALL:-${LC_MESSAGES:-zh_cn}}}}" | awk -F'[ .@]' '{print tolower($1)}' | sed 's/-/_/' 2>/dev/null)
|
||||
fi
|
||||
|
||||
translate() {
|
||||
# 处理特殊字符
|
||||
local lua_script=$(cat <<LUA
|
||||
require "luci.i18n".setlanguage("$lang")
|
||||
print(require "luci.i18n".translate([==[$1]==]))
|
||||
LUA
|
||||
)
|
||||
|
||||
lua -e "$lua_script"
|
||||
}
|
||||
|
||||
if [ "$1" == "clear_log" ]; then
|
||||
# 清空日志
|
||||
>"${logfile}"
|
||||
|
||||
elif [ "$1" == "child" ]; then
|
||||
shift
|
||||
command_name=$1
|
||||
shift
|
||||
"$command_name" "$@"
|
||||
fi
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"admin/control/timecontrol": {
|
||||
"title": "Time Control",
|
||||
"order": 10,
|
||||
"action": {
|
||||
"type": "firstchild"
|
||||
},
|
||||
"acl": [ "read" ],
|
||||
"depends": {
|
||||
"acl": [ "luci-app-timecontrol" ]
|
||||
},
|
||||
"recurse": true
|
||||
},
|
||||
"admin/control/timecontrol/basic": {
|
||||
"title": "Time Control",
|
||||
"order": 10,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "timecontrol/basic"
|
||||
},
|
||||
"acl": [ "read" ]
|
||||
},
|
||||
"admin/control/timecontrol/log": {
|
||||
"title": "Log",
|
||||
"order": 40,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "timecontrol/log"
|
||||
},
|
||||
"acl": [ "read" ]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"luci-app-timecontrol": {
|
||||
"description": "Grant UCI Internet time control for luci-app-timecontrol",
|
||||
"read": {
|
||||
|
||||
"ubus": {
|
||||
|
||||
"file": ["exec", "list", "stat", "read"],
|
||||
"uci": [ "*" ],
|
||||
"timecontrol": ["*"]
|
||||
}
|
||||
},
|
||||
"write": {
|
||||
"ubus": {
|
||||
"timecontrol": ["*"],
|
||||
"file": ["write"],
|
||||
"uci": ["*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user