From 009f8b91abd7da09aa22bfc314e297b4035b2c63 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 27 May 2026 00:45:19 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9B=85=20Sync=202026-05-27=2000:45:19?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ipv6-neigh/Makefile | 2 +- ipv6-neigh/src/main.rs | 7 +-- luci-app-ttl/Makefile | 4 +- luci-app-ttl/root/usr/share/ttl.sh | 36 +++++++++--- luci-app-ttl/root/usr/share/ttlipt.sh | 81 ++++++++++++++------------- luci-app-ttl/root/usr/share/ttlnft.sh | 77 +++++++++++++------------ 6 files changed, 113 insertions(+), 94 deletions(-) diff --git a/ipv6-neigh/Makefile b/ipv6-neigh/Makefile index f33930da..e1954023 100644 --- a/ipv6-neigh/Makefile +++ b/ipv6-neigh/Makefile @@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=ipv6-neigh PKG_VERSION:=0.1.0 -PKG_RELEASE:=14 +PKG_RELEASE:=15 PKG_BUILD_DEPENDS:=rust/host PKG_BUILD_PARALLEL:=1 diff --git a/ipv6-neigh/src/main.rs b/ipv6-neigh/src/main.rs index 5d4fd519..f70b5016 100644 --- a/ipv6-neigh/src/main.rs +++ b/ipv6-neigh/src/main.rs @@ -525,11 +525,8 @@ async fn main() -> Result<(), ()> { // reconcile_dns's AXFR pass cannot catch these because the record is // also absent from DNS, so no probe is ever triggered for them. if let Ok(neighbours) = dump_neighbours(handle.clone(), private_subnet_v4).await { - // Build a set of IPs that are currently REACHABLE in the kernel, - // used below to detect registered entries the kernel has marked FAILED. - let reachable_ips: std::collections::HashSet = neighbours + let all_dump_ips: std::collections::HashSet = neighbours .iter() - .filter(|n| n.state == NeighbourState::Reachable) .map(|n| inet_to_string(&n.inet)) .collect(); @@ -622,7 +619,7 @@ async fn main() -> Result<(), ()> { let to_remove: Vec<(String, String)> = registered .iter() .filter(|((_hostname, ip_str), entry)| { - !reachable_ips.contains(ip_str.as_str()) + !all_dump_ips.contains(ip_str.as_str()) && now.duration_since(entry.last_confirmed) > stale_cutoff }) .map(|(k, _)| k.clone()) diff --git a/luci-app-ttl/Makefile b/luci-app-ttl/Makefile index b4d663ec..5965fcf9 100644 --- a/luci-app-ttl/Makefile +++ b/luci-app-ttl/Makefile @@ -10,8 +10,8 @@ LUCI_TITLE:=Antitethering module for luci-app-firewall #LUCI_DEPENDS:= +luci-app-firewall PKG_LICENSE:=Apache-2.0 -PKG_VERSION:=0.0.5 -PKG_RELEASE:=1 +PKG_VERSION:=0.0.6 +PKG_RELEASE:=2 include $(TOPDIR)/feeds/luci/luci.mk diff --git a/luci-app-ttl/root/usr/share/ttl.sh b/luci-app-ttl/root/usr/share/ttl.sh index 48a0e59a..048b7c68 100755 --- a/luci-app-ttl/root/usr/share/ttl.sh +++ b/luci-app-ttl/root/usr/share/ttl.sh @@ -1,19 +1,37 @@ #!/bin/sh +. /lib/functions.sh +# handle_section is called by config_foreach for each UCI section of type "ttl". +# $1 = section name (e.g. "cfg1", or named section) +handle_section(){ + local s="$1" + config_get method "$s" method + config_get advanced "$s" advanced + config_get inet "$s" inet + config_get ports "$s" ports + config_get ttl "$s" ttl 64 + config_get iface "$s" iface + config_get proxy "$s" proxy -SECTIONS=$(echo $(uci show ttl | awk -F [\]\[\@=] '/=ttl/{print $3}')) + [ -n "$iface" ] && ifn="$iface" || ifn="lan" + DEV=$(ifstatus "$ifn" | jsonfilter -e '@["l3_device"]') -get_vars(){ - for v in method advanced inet ports ttl iface proxy; do - eval $v=$(uci -q get ttl.@ttl[${s}].${v} 2>/dev/nul) - done + case $method in + ttl) method_ttl ;; + proxy) method_proxy ;; + esac } +config_load ttl -# check iptables or nft -# -if [ -x /usr/sbin/iptables -o /usr/sbin/ip6tables -a ! -x /usr/sbin/nft ]; then +# Choose firewall backend: nft takes priority over iptables +if [ -x /usr/sbin/nft ]; then + . /usr/share/ttlnft.sh +elif [ -x /usr/sbin/iptables ] || [ -x /usr/sbin/ip6tables ]; then . /usr/share/ttlipt.sh else - . /usr/share/ttlnft.sh + logger -t ttl "No firewall backend found (nft/iptables/ip6tables)" + exit 1 fi + +config_foreach handle_section ttl diff --git a/luci-app-ttl/root/usr/share/ttlipt.sh b/luci-app-ttl/root/usr/share/ttlipt.sh index 967e1cce..78a43325 100755 --- a/luci-app-ttl/root/usr/share/ttlipt.sh +++ b/luci-app-ttl/root/usr/share/ttlipt.sh @@ -1,18 +1,48 @@ +# --- Input validation --- +validate_ttl(){ + echo "$1" | grep -qE '^([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$' || { + logger -t ttl "Invalid TTL value: '$1' (must be 0-255)"; exit 1 + } +} + +validate_ports(){ + # accepts: "all", "http", or comma-separated port numbers + case "$1" in + all|http|"") return 0 ;; + esac + echo "$1" | grep -qE '^[0-9]+(,[0-9]+)*$' || { + logger -t ttl "Invalid ports value: '$1'"; exit 1 + } +} + +validate_iface(){ + # interface name: alphanumeric, dash, dot, underscore, max 15 chars + echo "$1" | grep -qE '^[a-zA-Z0-9._-]{1,15}$' || { + logger -t ttl "Invalid iface value: '$1'"; exit 1 + } +} + +validate_proxy(){ + # IP:port or [IPv6]:port + echo "$1" | grep -qE '^(\[?[0-9a-fA-F:.]+\]?):([0-9]{1,5})$' || { + logger -t ttl "Invalid proxy value: '$1'"; exit 1 + } +} +# --- End validation --- + method_ttl(){ ttl=${ttl:=64} + validate_ttl "$ttl" + [ -n "$iface" ] && validate_iface "$iface" - #case $(($ttl % 2)) in - # 0) TTL_INC=$(($ttl-1)) ;; - # *) TTL_INC=$ttl ;; - #esac TTL_INC=$(($ttl-1)) for T in $IPT; do case $T in iptables) SUFFIX="TTL --ttl-set" - if [ $iface ]; then + if [ -n "$iface" ]; then $T -t mangle -A TTLFIX -i $DEV -m ttl --ttl 1 -j TTL --ttl-inc $TTL_INC else $T -t mangle -A TTLFIX -m ttl --ttl 1 -j TTL --ttl-inc $TTL_INC @@ -20,7 +50,7 @@ method_ttl(){ ;; ip6tables) SUFFIX="HL --hl-set" - if [ $iface ]; then + if [ -n "$iface" ]; then $T -t mangle -A TTLFIX -i $DEV -m hl --hl 1 -j HL --hl-inc $TTL_INC else $T -t mangle -A TTLFIX -m hl --hl 1 -j HL --hl-inc $TTL_INC @@ -28,7 +58,7 @@ method_ttl(){ ;; esac - if [ $iface ]; then + if [ -n "$iface" ]; then $T -t mangle -A TTL_OUT -o $DEV -j $SUFFIX $ttl $T -t mangle -A TTL_POST -o $DEV -j $SUFFIX $ttl else @@ -40,6 +70,9 @@ method_ttl(){ method_proxy(){ + validate_ports "$ports" + [ -n "$proxy" ] && validate_proxy "$proxy" + [ -n "$iface" ] && validate_iface "$iface" for T in $IPT; do [ "$proxy" ] && { @@ -51,11 +84,11 @@ method_proxy(){ } || { case $T in iptables) - IPADDR=$(ifstatus $ifn | jsonfilter -e '@["ipv4-address"][*]["address"]') + IPADDR=$(ifstatus "$ifn" | jsonfilter -e '@["ipv4-address"][*]["address"]') END="${IPADDR}:3128" ;; ip6tables) - for a in $(ifstatus $ifn | jsonfilter -e '@["ipv6-prefix-assignment"][*]["local-address"]["address"]'); do + for a in $(ifstatus "$ifn" | jsonfilter -e '@["ipv6-prefix-assignment"][*]["local-address"]["address"]'); do IPADDR="$a" done END="[$IPADDR]:3128" @@ -77,7 +110,7 @@ method_proxy(){ --dports 80,443 -j DNAT --to-destination $END ;; *) - if [ $ports ]; then + if [ -n "$ports" ]; then $T -t nat -A FIXPROXY ! -d ${IPADDR} \ ! -s ${IPADDR} -p tcp -m multiport \ --dports $ports -j DNAT --to-destination $END @@ -122,31 +155,3 @@ for T in $IPT; do $T -t nat -${a} PREROUTING -j PROXY done done - -for s in $SECTIONS; do - if [ "$s" ]; then - get_vars - else - exit 0 - fi - - [ -n $iface ] && { - ifn=$iface - } || { - ifn=lan - } - - case $inet in - ipv4) IPT="iptables" ;; - ipv6) IPT="ip6tables" ;; - *) IPT="iptables ip6tables";; - esac - if ! [ -f /lib/modules/$(uname -r)/ip6table_nat.ko ]; then - IPT="iptables" - fi - DEV=$(ifstatus $iface | jsonfilter -e '@["l3_device"]') - case $method in - ttl) method_ttl ;; - proxy) method_proxy ;; - esac -done diff --git a/luci-app-ttl/root/usr/share/ttlnft.sh b/luci-app-ttl/root/usr/share/ttlnft.sh index 619bcba0..820f8354 100755 --- a/luci-app-ttl/root/usr/share/ttlnft.sh +++ b/luci-app-ttl/root/usr/share/ttlnft.sh @@ -1,6 +1,37 @@ +# --- Input validation --- +validate_ttl(){ + echo "$1" | grep -qE '^([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$' || { + logger -t ttl "Invalid TTL value: '$1' (must be 0-255)"; exit 1 + } +} + +validate_ports(){ + case "$1" in + all|http|"") return 0 ;; + esac + echo "$1" | grep -qE '^[0-9]+(,[0-9]+)*$' || { + logger -t ttl "Invalid ports value: '$1'"; exit 1 + } +} + +validate_iface(){ + echo "$1" | grep -qE '^[a-zA-Z0-9._-]{1,15}$' || { + logger -t ttl "Invalid iface value: '$1'"; exit 1 + } +} + +validate_proxy(){ + echo "$1" | grep -qE '^(\[?[0-9a-fA-F:.]+\]?):([0-9]{1,5})$' || { + logger -t ttl "Invalid proxy value: '$1'"; exit 1 + } +} +# --- End validation --- + method_ttl(){ ttl=${ttl:=64} + validate_ttl "$ttl" + [ -n "$iface" ] && validate_iface "$iface" # create mangle table nft add table ip mangle 2>/dev/null @@ -20,7 +51,7 @@ method_ttl(){ ip) TTLNAME=ttl ;; ip6) TTLNAME=hoplimit ;; esac - if [ $iface ]; then + if [ -n "$iface" ]; then nft add rule $fam mangle TTLFIX iif $DEV $fam $TTLNAME 1 $fam $TTLNAME set $ttl nft add rule $fam mangle TTL_OUT oif $DEV $fam $TTLNAME set $ttl nft add rule $fam mangle TTL_POST oif $DEV $fam $TTLNAME set $ttl @@ -33,6 +64,9 @@ method_ttl(){ } method_proxy(){ + validate_ports "$ports" + [ -n "$proxy" ] && validate_proxy "$proxy" + [ -n "$iface" ] && validate_iface "$iface" for fam in $family; do # create nat table nft add table $fam nat 2>/dev/null @@ -46,11 +80,11 @@ method_proxy(){ # get ipaddress from iface if not defined case $fam in ip) - IPADDR=$(ifstatus $ifn | jsonfilter -e '@["ipv4-address"][*]["address"]') + IPADDR=$(ifstatus "$ifn" | jsonfilter -e '@["ipv4-address"][*]["address"]') END="${IPADDR}:3128" ;; ip6) - IPADDR=$(ifstatus $ifn | jsonfilter -e '@["ipv6-prefix-assignment"][*]["local-address"]["address"]' | head -n1) + IPADDR=$(ifstatus "$ifn" | jsonfilter -e '@["ipv6-prefix-assignment"][*]["local-address"]["address"]' | head -n1) END="${IPADDR}:3128" ;; esac @@ -61,7 +95,7 @@ method_proxy(){ nft add chain $fam nat FIXPROXY # add traffic rule - [ $iface ] && { + [ -n "$iface" ] && { nft add rule $fam nat PROXY iif $DEV jump FIXPROXY } || { nft add rule $fam nat PROXY jump FIXPROXY @@ -92,46 +126,11 @@ method_proxy(){ # init tables and chains for fml in ip ip6; do - # create table mangle nft delete table $fml mangle 2>/dev/null nft delete table $fml nat 2>/dev/null - # define chains nft add table $fml mangle nft add chain $fml mangle TTLFIX { type filter hook prerouting priority -150 \; } nft add chain $fml mangle TTL_OUT { type route hook output priority -150 \; } nft add chain $fml mangle TTL_POST { type filter hook postrouting priority -150 \; } done - -for s in $SECTIONS; do - if [ "$s" ]; then - get_vars - else - exit 0 - fi - - [ $iface ] && { - ifn=$iface - } || { - ifn=lan - } - - case $inet in - ipv4) family="ip" ;; - ipv6) family="ip6" ;; - *) family="ip ip6" ;; - esac - - #if [ ! -f /lib/modules/$(uname -r)/ip6table_nat.ko ]; then - # family="ip" - #fi - - DEV=$(ifstatus $iface | jsonfilter -e '@["l3_device"]') - - case $method in - ttl) method_ttl ;; - proxy) method_proxy ;; - esac - -done -