mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-07-27 10:31:38 +08:00
🌴 Sync 2026-05-27 21:11:55
This commit is contained in:
parent
3a6f6005dc
commit
dfd1e47fd7
@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=hickory-dns
|
||||
PKG_VERSION:=0.26.1
|
||||
PKG_RELEASE:=27
|
||||
PKG_RELEASE:=28
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/hickory-dns/hickory-dns/tar.gz/v$(PKG_VERSION)?
|
||||
|
||||
8
hickory-dns/files/etc/hickory-dns/0.0.10.zone
Normal file
8
hickory-dns/files/etc/hickory-dns/0.0.10.zone
Normal file
@ -0,0 +1,8 @@
|
||||
$TTL 3D
|
||||
@ IN SOA ns.lan. admin.lan. (
|
||||
1 ; Serial
|
||||
28800 ; Refresh
|
||||
7200 ; Retry
|
||||
604800 ; Expire
|
||||
86400) ; Minimum TTL
|
||||
NS ns.lan.
|
||||
8
hickory-dns/files/etc/hickory-dns/3.168.192.zone
Normal file
8
hickory-dns/files/etc/hickory-dns/3.168.192.zone
Normal file
@ -0,0 +1,8 @@
|
||||
$TTL 3D
|
||||
@ IN SOA ns.lan. admin.lan. (
|
||||
1 ; Serial
|
||||
28800 ; Refresh
|
||||
7200 ; Retry
|
||||
604800 ; Expire
|
||||
86400) ; Minimum TTL
|
||||
NS ns.lan.
|
||||
8
hickory-dns/files/etc/hickory-dns/d.f.ip6.arpa.zone
Normal file
8
hickory-dns/files/etc/hickory-dns/d.f.ip6.arpa.zone
Normal file
@ -0,0 +1,8 @@
|
||||
$TTL 3D
|
||||
@ IN SOA ns.lan. admin.lan. (
|
||||
1 ; Serial
|
||||
28800 ; Refresh
|
||||
7200 ; Retry
|
||||
604800 ; Expire
|
||||
86400) ; Minimum TTL
|
||||
NS ns.lan.
|
||||
@ -47,6 +47,57 @@ key_file = "/etc/hickory-dns/update.key"
|
||||
algorithm = "hmac-sha256"
|
||||
fudge = 300
|
||||
|
||||
[[zones]]
|
||||
zone = "d.f.ip6.arpa"
|
||||
zone_type = "Primary"
|
||||
axfr_policy = "AllowAll"
|
||||
|
||||
[zones.stores]
|
||||
type = "sqlite"
|
||||
zone_path = "/etc/hickory-dns/d.f.ip6.arpa.zone"
|
||||
journal_path = ":memory:"
|
||||
allow_update = true
|
||||
|
||||
[[zones.stores.tsig_keys]]
|
||||
name = "update-key."
|
||||
key_file = "/etc/hickory-dns/update.key"
|
||||
algorithm = "hmac-sha256"
|
||||
fudge = 300
|
||||
|
||||
[[zones]]
|
||||
zone = "0.0.10.in-addr.arpa"
|
||||
zone_type = "Primary"
|
||||
axfr_policy = "AllowAll"
|
||||
|
||||
[zones.stores]
|
||||
type = "sqlite"
|
||||
zone_path = "/etc/hickory-dns/0.0.10.zone"
|
||||
journal_path = ":memory:"
|
||||
allow_update = true
|
||||
|
||||
[[zones.stores.tsig_keys]]
|
||||
name = "update-key."
|
||||
key_file = "/etc/hickory-dns/update.key"
|
||||
algorithm = "hmac-sha256"
|
||||
fudge = 300
|
||||
|
||||
[[zones]]
|
||||
zone = "3.168.192.in-addr.arpa"
|
||||
zone_type = "Primary"
|
||||
axfr_policy = "AllowAll"
|
||||
|
||||
[zones.stores]
|
||||
type = "sqlite"
|
||||
zone_path = "/etc/hickory-dns/3.168.192.zone"
|
||||
journal_path = ":memory:"
|
||||
allow_update = true
|
||||
|
||||
[[zones.stores.tsig_keys]]
|
||||
name = "update-key."
|
||||
key_file = "/etc/hickory-dns/update.key"
|
||||
algorithm = "hmac-sha256"
|
||||
fudge = 300
|
||||
|
||||
[[zones]]
|
||||
zone = "."
|
||||
zone_type = "External"
|
||||
|
||||
@ -11,7 +11,10 @@ i=0
|
||||
while [ "$i" -lt "$RETRY" ]; do
|
||||
if ss -lnt | grep -q ':53'; then
|
||||
logger -t ipv6-neigh-wrapper "DNS port 53 ready, starting ipv6-neigh"
|
||||
exec "$NEIGH_PROG" --private-subnet-v4 -d "[::1]:53" -z lan --router-alias router --keepalive-gua
|
||||
exec "$NEIGH_PROG" --private-subnet-v4 -d "[::1]:53" -z lan --router-alias router --keepalive-gua \
|
||||
--ptr-ipv4-subnet 10.0.0.0/24 \
|
||||
--ptr-ipv4-subnet 192.168.3.0/24 \
|
||||
--ptr-ula
|
||||
fi
|
||||
i=$((i + 1))
|
||||
sleep 1
|
||||
|
||||
111
ipv6-neigh/Cargo.lock
generated
111
ipv6-neigh/Cargo.lock
generated
@ -189,9 +189,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -227,16 +227,6 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.9"
|
||||
@ -534,7 +524,13 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ip-neigh"
|
||||
name = "ipnet"
|
||||
version = "2.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
|
||||
|
||||
[[package]]
|
||||
name = "ipv6-neigh"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
@ -552,12 +548,6 @@ dependencies = [
|
||||
"ubus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.2"
|
||||
@ -572,9 +562,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "jiff"
|
||||
version = "0.2.24"
|
||||
version = "0.2.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f00b5dbd620d61dfdcb6007c9c1f6054ebd75319f163d886a9055cec1155073d"
|
||||
checksum = "392c70591e8749fe235ddaf513e6f58b26bce3dcc16524cecc8936f75afa161e"
|
||||
dependencies = [
|
||||
"jiff-static",
|
||||
"log",
|
||||
@ -585,9 +575,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jiff-static"
|
||||
version = "0.2.24"
|
||||
version = "0.2.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e000de030ff8022ea1da3f466fbb0f3a809f5e51ed31f6dd931c35181ad8e6d7"
|
||||
checksum = "47b605b0c050d845fc355bb11eb3f9a8deddc218ea60c76e61aa1f2adfb2c96a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -612,26 +602,17 @@ version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
version = "0.4.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.0"
|
||||
version = "2.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
@ -706,9 +687,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967"
|
||||
checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
@ -726,29 +707,6 @@ version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
@ -848,15 +806,6 @@ version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.3"
|
||||
@ -927,12 +876,6 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.28"
|
||||
@ -971,9 +914,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.149"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
@ -988,16 +931,6 @@ version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
|
||||
dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.12"
|
||||
@ -1153,9 +1086,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.61.2",
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "ip-neigh"
|
||||
name = "ipv6-neigh"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
@ -8,14 +8,14 @@ edition = "2024"
|
||||
[dependencies]
|
||||
rtnetlink = { version = "0.21.0" }
|
||||
netlink-sys = { version = "0.8" }
|
||||
tokio = { version = "1.52.3", features = ["full"]}
|
||||
tokio = { version = "1.52.3", default-features = false, features = ["rt", "macros", "net", "time", "io-util"] }
|
||||
futures = "0.3.32"
|
||||
netlink-packet-route = {version = "0.30" }
|
||||
netlink-packet-core= {version ="0.8"}
|
||||
clap = { version = "4.6", default-features = false, features = ["cargo", "derive", "help", "std", "suggestions"] }
|
||||
hickory-proto = { git = "https://github.com/hickory-dns/hickory-dns.git", rev = "ee7ba64757eca61d86f323ef992eba482605d7c3", features = ["dnssec-ring"] }
|
||||
ubus = "0.1.7"
|
||||
serde_json = "1.0.149"
|
||||
serde_json = "1.0.150"
|
||||
log = "0.4"
|
||||
env_logger = "0.11"
|
||||
socket2 = { version = "0.6", features = ["all"] }
|
||||
@ -25,3 +25,5 @@ opt-level = 3
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
strip = true
|
||||
overflow-checks = false
|
||||
panic = "abort"
|
||||
|
||||
@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=ipv6-neigh
|
||||
PKG_VERSION:=0.1.0
|
||||
PKG_RELEASE:=15
|
||||
PKG_RELEASE:=16
|
||||
|
||||
PKG_BUILD_DEPENDS:=rust/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
@ -12,6 +12,16 @@ RUST_PKG_LOCKED:=0
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(TOPDIR)/feeds/packages/lang/rust/rust-package.mk
|
||||
|
||||
# Derive Rust CPU flags from TARGET_CFLAGS (e.g. -mcpu=cortex-a53+crc+crypto)
|
||||
_comma:=,
|
||||
_empty:=
|
||||
_space:=$(_empty) $(_empty)
|
||||
_mcpu:=$(patsubst -mcpu=%,%,$(filter -mcpu=%,$(TARGET_CFLAGS)))
|
||||
_mcpu_parts:=$(subst +,$(_space),$(_mcpu))
|
||||
_cpu_base:=$(firstword $(_mcpu_parts))
|
||||
_cpu_feats:=$(subst $(_space),$(_comma),$(addprefix +,$(wordlist 2,99,$(_mcpu_parts))))
|
||||
PKG_RUST_CPU_FLAGS:=$(if $(_cpu_base),-Ctarget-cpu=$(_cpu_base)$(if $(_cpu_feats), -Ctarget-feature=$(_cpu_feats)))
|
||||
|
||||
define Package/ipv6-neigh
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
@ -37,6 +47,10 @@ endef
|
||||
|
||||
define Build/Compile
|
||||
+$(CARGO_PKG_VARS) \
|
||||
CARGO_PROFILE_RELEASE_OPT_LEVEL=3 \
|
||||
CARGO_PROFILE_RELEASE_OVERFLOW_CHECKS=false \
|
||||
CARGO_PROFILE_RELEASE_PANIC=abort \
|
||||
RUSTFLAGS="$(CARGO_RUSTFLAGS) $(PKG_RUST_CPU_FLAGS)" \
|
||||
cargo build -v --profile $(CARGO_PKG_PROFILE) \
|
||||
--manifest-path "$(PKG_BUILD_DIR)/Cargo.toml" \
|
||||
--target $(RUSTC_TARGET_ARCH) \
|
||||
@ -44,12 +58,12 @@ define Build/Compile
|
||||
$(if $(filter --jobserver%,$(PKG_JOBS)),,-j1) \
|
||||
$(CARGO_PKG_ARGS)
|
||||
+$(INSTALL_DIR) $(PKG_INSTALL_DIR)/bin/
|
||||
+$(INSTALL_BIN) $(PKG_BUILD_DIR)/target/$(RUSTC_TARGET_ARCH)/$(CARGO_PKG_PROFILE)/ip-neigh $(PKG_INSTALL_DIR)/bin/
|
||||
+$(INSTALL_BIN) $(PKG_BUILD_DIR)/target/$(RUSTC_TARGET_ARCH)/$(CARGO_PKG_PROFILE)/ipv6-neigh $(PKG_INSTALL_DIR)/bin/
|
||||
endef
|
||||
|
||||
define Package/ipv6-neigh/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin/
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/bin/ip-neigh $(1)/usr/bin/ipv6-neigh
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/bin/ipv6-neigh $(1)/usr/bin/ipv6-neigh
|
||||
endef
|
||||
|
||||
$(eval $(call RustBinPackage,ipv6-neigh))
|
||||
|
||||
@ -2,7 +2,8 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use hickory_proto::op::{Message, Query, ResponseCode, update_message};
|
||||
use hickory_proto::rr::{Name, RData, RecordSet, RecordType, TSigner};
|
||||
use hickory_proto::rr::{Name, RData, Record, RecordSet, RecordType, TSigner};
|
||||
use hickory_proto::rr::rdata::PTR as PtrRData;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::time::timeout;
|
||||
@ -23,11 +24,100 @@ pub(crate) struct DnsUpdater {
|
||||
server_addr: SocketAddr,
|
||||
zone: Name,
|
||||
signer: TSigner,
|
||||
/// (network as u32, prefix_len, reverse zone name) for IPv4 PTR
|
||||
ipv4_ptr_zones: Vec<(u32, u8, Name)>,
|
||||
/// Reverse zone for ULA IPv6 (d.f.ip6.arpa)
|
||||
ula_ptr_zone: Option<Name>,
|
||||
}
|
||||
|
||||
impl DnsUpdater {
|
||||
pub fn new(server_addr: SocketAddr, zone: Name, signer: TSigner) -> Self {
|
||||
Self { server_addr, zone, signer }
|
||||
Self { server_addr, zone, signer, ipv4_ptr_zones: vec![], ula_ptr_zone: None }
|
||||
}
|
||||
|
||||
/// Configure reverse PTR zones.
|
||||
/// `ipv4_subnets`: list of (network_addr, prefix_len); /8, /16, /24 boundaries supported.
|
||||
/// `ula`: if true, enables ULA PTR via `d.f.ip6.arpa`.
|
||||
pub fn with_ptr_zones(mut self, ipv4_subnets: &[(Ipv4Addr, u8)], ula: bool) -> Self {
|
||||
self.ipv4_ptr_zones = ipv4_subnets
|
||||
.iter()
|
||||
.map(|(net, prefix_len)| (u32::from(*net), *prefix_len, ipv4_zone_name(*net, *prefix_len)))
|
||||
.collect();
|
||||
if ula {
|
||||
self.ula_ptr_zone = Some(Name::from_ascii("d.f.ip6.arpa.").expect("always valid"));
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Upsert a PTR record. Silently skips IPs with no configured reverse zone.
|
||||
pub async fn upsert_ptr(
|
||||
&self,
|
||||
addr: IpAddr,
|
||||
hostname: &str,
|
||||
ttl: u32,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let (ptr_name, rev_zone) = match addr {
|
||||
IpAddr::V4(ip) => {
|
||||
let Some(zone) = self.find_ipv4_ptr_zone(ip).cloned() else { return Ok(()) };
|
||||
(ipv4_ptr_name(ip), zone)
|
||||
}
|
||||
IpAddr::V6(ip) if (ip.segments()[0] & 0xfe00) == 0xfc00 => {
|
||||
let Some(zone) = self.ula_ptr_zone.clone() else { return Ok(()) };
|
||||
(ipv6_ptr_name(ip), zone)
|
||||
}
|
||||
_ => return Ok(()),
|
||||
};
|
||||
let target = Name::from_ascii(hostname)?.append_domain(&self.zone)?;
|
||||
|
||||
// Delete any existing PTR rrset first (replace semantics: one PTR per IP).
|
||||
let del_record = Record::update0(ptr_name.clone(), 0, RecordType::PTR);
|
||||
let del_msg = update_message::delete_rrset(del_record, rev_zone.clone(), false);
|
||||
let del_resp = self.send_tcp(del_msg).await?;
|
||||
check_response(&del_resp, &[])?;
|
||||
|
||||
// Add the new PTR.
|
||||
let mut rrset = RecordSet::with_ttl(ptr_name, RecordType::PTR, ttl);
|
||||
rrset.add_rdata(RData::PTR(PtrRData(target)));
|
||||
let msg = update_message::append(rrset, rev_zone, false, false);
|
||||
let response = self.send_tcp(msg).await?;
|
||||
check_response(&response, &[])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete a PTR record. Silently skips IPs with no configured reverse zone.
|
||||
pub async fn delete_ptr(
|
||||
&self,
|
||||
addr: IpAddr,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let (ptr_name, rev_zone) = match addr {
|
||||
IpAddr::V4(ip) => {
|
||||
let Some(zone) = self.find_ipv4_ptr_zone(ip).cloned() else { return Ok(()) };
|
||||
(ipv4_ptr_name(ip), zone)
|
||||
}
|
||||
IpAddr::V6(ip) if (ip.segments()[0] & 0xfe00) == 0xfc00 => {
|
||||
let Some(zone) = self.ula_ptr_zone.clone() else { return Ok(()) };
|
||||
(ipv6_ptr_name(ip), zone)
|
||||
}
|
||||
_ => return Ok(()),
|
||||
};
|
||||
let record = Record::update0(ptr_name, 0, RecordType::PTR);
|
||||
let msg = update_message::delete_rrset(record, rev_zone, false);
|
||||
let response = self.send_tcp(msg).await?;
|
||||
check_response(&response, &[])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn find_ipv4_ptr_zone(&self, addr: Ipv4Addr) -> Option<&Name> {
|
||||
let addr_u32 = u32::from(addr);
|
||||
self.ipv4_ptr_zones
|
||||
.iter()
|
||||
.filter(|(net, prefix_len, _)| {
|
||||
if *prefix_len == 0 { return true; }
|
||||
let shift = 32u8.saturating_sub(*prefix_len);
|
||||
(addr_u32 >> shift) == (net >> shift)
|
||||
})
|
||||
.max_by_key(|(_, prefix_len, _)| *prefix_len)
|
||||
.map(|(_, _, name)| name)
|
||||
}
|
||||
|
||||
/// Create or append a AAAA record.
|
||||
@ -214,6 +304,41 @@ impl DnsUpdater {
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the PTR owner name for an IPv4 address.
|
||||
/// e.g. 192.168.3.5 → `5.3.168.192.in-addr.arpa.`
|
||||
fn ipv4_ptr_name(addr: Ipv4Addr) -> Name {
|
||||
let o = addr.octets();
|
||||
Name::from_ascii(&format!("{}.{}.{}.{}.in-addr.arpa.", o[3], o[2], o[1], o[0]))
|
||||
.expect("always valid")
|
||||
}
|
||||
|
||||
/// Build the PTR owner name for an IPv6 address (nibble-reversed).
|
||||
fn ipv6_ptr_name(addr: Ipv6Addr) -> Name {
|
||||
let nibbles: String = addr
|
||||
.octets()
|
||||
.iter()
|
||||
.rev()
|
||||
.flat_map(|b| {
|
||||
let lo = char::from_digit((b & 0x0f) as u32, 16).unwrap();
|
||||
let hi = char::from_digit((b >> 4) as u32, 16).unwrap();
|
||||
[lo, '.', hi, '.']
|
||||
})
|
||||
.collect();
|
||||
Name::from_ascii(&format!("{}ip6.arpa.", nibbles)).expect("always valid")
|
||||
}
|
||||
|
||||
/// Derive the reverse zone name for an IPv4 subnet (only /8, /16, /24 boundaries).
|
||||
fn ipv4_zone_name(net: Ipv4Addr, prefix_len: u8) -> Name {
|
||||
let o = net.octets();
|
||||
let s = match prefix_len {
|
||||
24..=32 => format!("{}.{}.{}.in-addr.arpa.", o[2], o[1], o[0]),
|
||||
16..=23 => format!("{}.{}.in-addr.arpa.", o[1], o[0]),
|
||||
8..=15 => format!("{}.in-addr.arpa.", o[0]),
|
||||
_ => "in-addr.arpa.".to_string(),
|
||||
};
|
||||
Name::from_ascii(&s).expect("always valid")
|
||||
}
|
||||
|
||||
/// Extract the single label that precedes the zone apex from a fully-qualified record name.
|
||||
///
|
||||
/// e.g. `"foo.lan."` with zone `"lan."` → `Some("foo")`.
|
||||
|
||||
@ -112,6 +112,9 @@ pub(crate) async fn register_router_addresses(
|
||||
Err(e) => warn!("failed to register router AAAA {} for {}: {}", addr, name, e),
|
||||
}
|
||||
}
|
||||
if let Err(e) = updater.upsert_ptr(IpAddr::V6(*addr), hostname, DEFAULT_TTL).await {
|
||||
warn!("failed to register router PTR for {}: {}", addr, e);
|
||||
}
|
||||
}
|
||||
IpAddr::V4(addr) => {
|
||||
for name in std::iter::once(hostname).chain(extra_alias) {
|
||||
@ -120,6 +123,9 @@ pub(crate) async fn register_router_addresses(
|
||||
Err(e) => warn!("failed to register router A {} for {}: {}", addr, name, e),
|
||||
}
|
||||
}
|
||||
if let Err(e) = updater.upsert_ptr(IpAddr::V4(*addr), hostname, DEFAULT_TTL).await {
|
||||
warn!("failed to register router PTR for {}: {}", addr, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddr as StdSocketAddr;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr as StdSocketAddr};
|
||||
use std::time::Instant;
|
||||
|
||||
use futures::stream::StreamExt;
|
||||
@ -80,6 +80,12 @@ struct Cli {
|
||||
/// Maximum number of ULA AAAA records to publish per host (oldest pruned first)
|
||||
#[clap(long, default_value = "2")]
|
||||
max_ula_per_host: usize,
|
||||
/// IPv4 subnets for PTR reverse DNS (CIDR notation, e.g. "192.168.3.0/24", repeatable)
|
||||
#[clap(long, value_name = "SUBNET")]
|
||||
ptr_ipv4_subnet: Vec<String>,
|
||||
/// Enable PTR reverse DNS for ULA IPv6 addresses (fc00::/7) via d.f.ip6.arpa
|
||||
#[clap(long)]
|
||||
ptr_ula: bool,
|
||||
}
|
||||
|
||||
|
||||
@ -96,6 +102,22 @@ fn is_failed_state(state: NeighbourState) -> bool {
|
||||
matches!(state, NeighbourState::Failed)
|
||||
}
|
||||
|
||||
/// Parse an IPv4 CIDR string like "192.168.3.0/24" into (network_addr, prefix_len).
|
||||
/// Panics with a clear message on invalid input.
|
||||
fn parse_ipv4_cidr(s: &str) -> (Ipv4Addr, u8) {
|
||||
let (addr_str, prefix_str) = s.split_once('/').unwrap_or_else(|| {
|
||||
panic!("invalid --ptr-ipv4-subnet \"{s}\": expected CIDR notation like \"192.168.3.0/24\"")
|
||||
});
|
||||
let addr: Ipv4Addr = addr_str.parse().unwrap_or_else(|_| {
|
||||
panic!("invalid --ptr-ipv4-subnet \"{s}\": \"{addr_str}\" is not a valid IPv4 address")
|
||||
});
|
||||
let prefix_len: u8 = prefix_str.parse().unwrap_or_else(|_| {
|
||||
panic!("invalid --ptr-ipv4-subnet \"{s}\": prefix length must be 0–32")
|
||||
});
|
||||
assert!(prefix_len <= 32, "invalid --ptr-ipv4-subnet \"{s}\": prefix length {prefix_len} > 32");
|
||||
(addr, prefix_len)
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> Result<(), ()> {
|
||||
let args = Cli::parse();
|
||||
@ -126,7 +148,12 @@ async fn main() -> Result<(), ()> {
|
||||
let signer = TSigner::new(key_data, TsigAlgorithm::HmacSha256, key_name, 300)
|
||||
.expect("invalid TSIG key");
|
||||
|
||||
let updater = db::DnsUpdater::new(args.dns_server, zone, signer);
|
||||
let ipv4_ptr_subnets: Vec<(Ipv4Addr, u8)> = args.ptr_ipv4_subnet
|
||||
.iter()
|
||||
.map(|s| parse_ipv4_cidr(s))
|
||||
.collect();
|
||||
let updater = db::DnsUpdater::new(args.dns_server, zone, signer)
|
||||
.with_ptr_zones(&ipv4_ptr_subnets, args.ptr_ula);
|
||||
|
||||
// Wait for the DNS server to become available before sending any updates.
|
||||
wait_for_dns_server(args.dns_server).await;
|
||||
@ -274,6 +301,16 @@ async fn main() -> Result<(), ()> {
|
||||
match result {
|
||||
Ok(()) => {
|
||||
info!("DNS update: added {} -> {:?}", hostname, neigh.inet);
|
||||
let ip = match &neigh.inet {
|
||||
NeighbourAddress::Inet6(addr) => Some(IpAddr::V6(*addr)),
|
||||
NeighbourAddress::Inet(addr) => Some(IpAddr::V4(*addr)),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(ip) = ip {
|
||||
if let Err(e) = updater.upsert_ptr(ip, hostname, DEFAULT_TTL).await {
|
||||
warn!("PTR update failed for {}: {}", hostname, e);
|
||||
}
|
||||
}
|
||||
registered.insert(key, RegisteredEntry {
|
||||
hostname: hostname.clone(),
|
||||
last_confirmed: Instant::now(),
|
||||
@ -416,18 +453,22 @@ async fn main() -> Result<(), ()> {
|
||||
// Resolve key by hostname (handles MAC changes: new MAC in leases
|
||||
// can now evict entries registered under an old MAC).
|
||||
// Fall back to scanning registered by IP if MAC is no longer in leases.
|
||||
debug!("Neighbour failed: {:?}", neigh);
|
||||
let key_opt: Option<(String, String)> = leases
|
||||
.get(&neigh.mac)
|
||||
.map(|h| (h.clone(), ip_str.clone()))
|
||||
.or_else(|| registered.keys().find(|(_, ip)| ip == &ip_str).cloned());
|
||||
if let Some(key) = key_opt {
|
||||
if let Some(entry) = registered.remove(&key) {
|
||||
debug!("Neighbour failed: {:?}", neigh);
|
||||
if !do_delete_dns(&entry.hostname, &neigh.inet, &updater).await {
|
||||
// DNS delete failed, put back so we retry
|
||||
registered.insert(key, entry);
|
||||
}
|
||||
} else {
|
||||
debug!("Neighbour failed: not in registered map, key={:?}", key);
|
||||
}
|
||||
} else {
|
||||
debug!("Neighbour failed: no registered key for ip={}", ip_str);
|
||||
}
|
||||
} else if neigh.state == NeighbourState::Reachable {
|
||||
if let Some(hostname) = leases.get(&neigh.mac) {
|
||||
|
||||
@ -33,12 +33,12 @@ fn compute_icmpv4_checksum(packet: &mut [u8]) {
|
||||
}
|
||||
|
||||
pub(crate) fn send_icmpv6_echo(addr: Ipv6Addr, ifindex: u32) -> std::io::Result<()> {
|
||||
let socket = Socket::new(Domain::IPV6, Type::DGRAM, Some(Protocol::ICMPV6))?;
|
||||
let socket = Socket::new(Domain::IPV6, Type::RAW, Some(Protocol::ICMPV6))?;
|
||||
socket.set_nonblocking(true)?;
|
||||
// Bind outgoing packet to the specific interface via IPV6_UNICAST_IF so the
|
||||
// kernel NUD state machine updates the correct neighbour entry.
|
||||
set_ipv6_unicast_if(&socket, ifindex)?;
|
||||
// ICMPv6 Echo Request: type=128, code=0, checksum=0 (kernel computes), id=0, seq=1
|
||||
// ICMPv6 Echo Request: type=128, code=0, checksum=0 (kernel computes for RAW ICMPV6), id=0, seq=1
|
||||
let packet: [u8; 8] = [128, 0, 0, 0, 0, 0, 0, 1];
|
||||
let dest = SockAddr::from(std::net::SocketAddrV6::new(addr, 0, 0, 0));
|
||||
socket.send_to(&packet, &dest)?;
|
||||
@ -46,7 +46,7 @@ pub(crate) fn send_icmpv6_echo(addr: Ipv6Addr, ifindex: u32) -> std::io::Result<
|
||||
}
|
||||
|
||||
pub(crate) fn send_icmpv4_echo(addr: Ipv4Addr, ifindex: u32) -> std::io::Result<()> {
|
||||
let socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::ICMPV4))?;
|
||||
let socket = Socket::new(Domain::IPV4, Type::RAW, Some(Protocol::ICMPV4))?;
|
||||
socket.set_nonblocking(true)?;
|
||||
// Bind outgoing packet to the specific interface via IP_UNICAST_IF.
|
||||
set_ip_unicast_if(&socket, ifindex)?;
|
||||
|
||||
@ -35,6 +35,14 @@ pub(crate) async fn process_new_neigh(
|
||||
match result {
|
||||
Ok(()) => {
|
||||
info!("DNS update: added {} -> {:?}", hostname, neigh.inet);
|
||||
let ip = match &neigh.inet {
|
||||
NeighbourAddress::Inet6(addr) => IpAddr::V6(*addr),
|
||||
NeighbourAddress::Inet(addr) => IpAddr::V4(*addr),
|
||||
_ => return true,
|
||||
};
|
||||
if let Err(e) = updater.upsert_ptr(ip, hostname, DEFAULT_TTL).await {
|
||||
warn!("PTR update failed for {} ({}): {}", hostname, ip, e);
|
||||
}
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
@ -58,6 +66,14 @@ pub(crate) async fn do_delete_dns(
|
||||
match result {
|
||||
Ok(()) => {
|
||||
info!("DNS update: removed {} -> {:?}", hostname, inet);
|
||||
let ip = match inet {
|
||||
NeighbourAddress::Inet6(addr) => IpAddr::V6(*addr),
|
||||
NeighbourAddress::Inet(addr) => IpAddr::V4(*addr),
|
||||
_ => return true,
|
||||
};
|
||||
if let Err(e) = updater.delete_ptr(ip).await {
|
||||
warn!("PTR delete failed for {} ({}): {}", hostname, ip, e);
|
||||
}
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
@ -172,7 +188,12 @@ pub(crate) async fn reconcile_dns(
|
||||
IpAddr::V4(addr) => updater.delete_a(hostname, *addr).await,
|
||||
};
|
||||
match result {
|
||||
Ok(()) => info!("reconcile: deleted stale DNS {} -> {}", hostname, ip),
|
||||
Ok(()) => {
|
||||
info!("reconcile: deleted stale DNS {} -> {}", hostname, ip);
|
||||
if let Err(e) = updater.delete_ptr(*ip).await {
|
||||
warn!("reconcile: PTR delete failed for stale {} {}: {}", hostname, ip, e);
|
||||
}
|
||||
}
|
||||
Err(e) => warn!("reconcile: failed to delete stale DNS {} {}: {}", hostname, ip, e),
|
||||
}
|
||||
}
|
||||
@ -198,7 +219,14 @@ pub(crate) async fn reconcile_dns(
|
||||
_ => continue,
|
||||
};
|
||||
match del_result {
|
||||
Ok(()) => info!("reconcile: deleted orphan DNS {} -> {}", hostname, ip_str),
|
||||
Ok(()) => {
|
||||
info!("reconcile: deleted orphan DNS {} -> {}", hostname, ip_str);
|
||||
if let Ok(ip) = ip_str.parse::<IpAddr>() {
|
||||
if let Err(e) = updater.delete_ptr(ip).await {
|
||||
warn!("reconcile: PTR delete failed for orphan {} {}: {}", hostname, ip_str, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => warn!("reconcile: failed to delete orphan {} {}: {}", hostname, ip_str, e),
|
||||
}
|
||||
// Probe so alive devices trigger a REACHABLE event and re-register.
|
||||
@ -234,6 +262,11 @@ pub(crate) async fn reconcile_dns(
|
||||
match result {
|
||||
Ok(()) => {
|
||||
info!("reconcile: re-pushed {} -> {}", hostname, ip_str);
|
||||
if let Ok(ip) = ip_str.parse::<IpAddr>() {
|
||||
if let Err(e) = updater.upsert_ptr(ip, hostname, DEFAULT_TTL).await {
|
||||
warn!("reconcile: PTR re-push failed for {} {}: {}", hostname, ip_str, e);
|
||||
}
|
||||
}
|
||||
entry.last_confirmed = Instant::now();
|
||||
}
|
||||
Err(e) => warn!("reconcile: failed to re-push {} {}: {}", hostname, ip_str, e),
|
||||
|
||||
@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-passwall
|
||||
PKG_VERSION:=26.5.20
|
||||
PKG_RELEASE:=138
|
||||
PKG_RELEASE:=139
|
||||
PKG_PO_VERSION:=$(PKG_VERSION)
|
||||
|
||||
PKG_CONFIG_DEPENDS:= \
|
||||
|
||||
@ -194,7 +194,7 @@ config_lines = {
|
||||
tonumber(LISTEN_PORT) ~= 0 and "bind [::]:" .. LISTEN_PORT .. "@lo" or "",
|
||||
(tonumber(LOCAL_PORT) ~= 0 and LOCAL_GROUP) and "bind [::]:" .. LOCAL_PORT .. "@lo -group " .. LOCAL_GROUP or "",
|
||||
tonumber(force_https_soa) == 1 and "force-qtype-SOA 65" or "force-qtype-SOA -,65",
|
||||
"server 114.114.114.114 -bootstrap-dns",
|
||||
"server 223.5.5.5 -bootstrap-dns",
|
||||
is_file_nonzero("/etc/hosts") and "hosts-file /etc/hosts" or "",
|
||||
DNS_MODE == "socks" and string.format("proxy-server socks5://%s -name %s", REMOTE_PROXY_SERVER, proxy_server_name) or ""
|
||||
}
|
||||
|
||||
@ -10,8 +10,8 @@ LUCI_TITLE:=Luci app for ssw
|
||||
LUCI_DEPENDS:= +luci-app-modeminfo
|
||||
|
||||
PKG_LICENSE:=GPLv3
|
||||
PKG_VERSION:=0.0.1
|
||||
PKG_RELEASE:=1
|
||||
PKG_VERSION:=0.0.2
|
||||
PKG_RELEASE:=2
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
|
||||
@ -37,9 +37,9 @@ return view.extend({
|
||||
_('Revert to default sim slot. Each failed attempt doubles revert time.'));
|
||||
o.depends({enable: '1'});
|
||||
|
||||
o = s.option(form.ListValue, 'rsrp', _('RSRP value'),
|
||||
_('Switch sim lower by value.'));
|
||||
for (var rsrp = -120; rsrp <= -80; rsrp++) {
|
||||
o = s.option(form.ListValue, 'rsrp', _('RSRP/RSCP value'),
|
||||
_('Switch sim lower by value. For 4G/5G is RSRP, for 3G RSCP.'));
|
||||
for (var rsrp = -120; rsrp <= -50; rsrp++) {
|
||||
o.value(rsrp, rsrp +' '+ _('dBm'));
|
||||
};
|
||||
o.depends({enable: '1'});
|
||||
|
||||
@ -22,11 +22,11 @@ msgstr "Вернуть"
|
||||
msgid "Revert to default sim slot. Each failed attempt doubles revert time."
|
||||
msgstr "Вернуть на SIM слот по-умолчанию. Каждая неудачная попытка увеличивает время возврата."
|
||||
|
||||
msgid "RSRP value"
|
||||
msgstr "Значение RSRP"
|
||||
msgid "RSRP/RSCP value"
|
||||
msgstr "Значение RSRP/RSCP"
|
||||
|
||||
msgid "Switch sim lower by value."
|
||||
msgstr "Переключить sim ниже значения."
|
||||
msgid "Switch sim lower by value. For 4G/5G is RSRP, for 3G RSCP."
|
||||
msgstr "Переключить sim ниже значения. Для 4G/5G значение RSRP, для 3G - RSCP."
|
||||
|
||||
msgid "Interval check. sec"
|
||||
msgstr "Интервал проверки. сек"
|
||||
|
||||
@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
LUCI_NAME:=luci-app-tinyfilemanager
|
||||
PKG_VERSION:=2.6
|
||||
PKG_RELEASE:=1
|
||||
PKG_RELEASE:=2
|
||||
|
||||
LUCI_TITLE:=LuCI Tiny File Manager
|
||||
LUCI_DEPENDS:=+php8 +php8-cgi +php8-fastcgi +php8-fpm +php8-mod-session +php8-mod-ctype +php8-mod-fileinfo +php8-mod-zip +php8-mod-iconv +php8-mod-mbstring +coreutils-stat +zoneinfo-asia +bash +curl +tar +inotifywait
|
||||
|
||||
@ -1,33 +1,33 @@
|
||||
location = /tinyfilemanager/ {
|
||||
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
|
||||
if (!-f $document_root$fastcgi_script_name) {
|
||||
return 404;
|
||||
location /tinyfilemanager/ {
|
||||
index index.php;
|
||||
|
||||
location ~ \.php$ {
|
||||
try_files $uri =404;
|
||||
|
||||
# Mitigate https://httpoxy.org/ vulnerabilities
|
||||
fastcgi_param HTTP_PROXY "";
|
||||
|
||||
#error_log /dev/null;
|
||||
fastcgi_connect_timeout 300s;
|
||||
fastcgi_read_timeout 300s;
|
||||
fastcgi_send_timeout 300s;
|
||||
fastcgi_buffer_size 32k;
|
||||
fastcgi_buffers 4 32k;
|
||||
fastcgi_busy_buffers_size 32k;
|
||||
fastcgi_temp_file_write_size 32k;
|
||||
client_body_timeout 10s;
|
||||
send_timeout 60s; # default, increase if experiencing a lot of timeouts.
|
||||
output_buffers 1 32k;
|
||||
include fastcgi_params;
|
||||
|
||||
# Only throw it at PHP-FPM if file exists (prevents PHP exploits).
|
||||
fastcgi_pass 127.0.0.1:1026; # or: unix:/var/run/php-fpm.sock;
|
||||
# fastcgi_pass unix:/var/run/php8-fpm.sock;
|
||||
|
||||
# SCRIPT_FILENAME parameter is used for PHP FPM determining
|
||||
# the script name. If it is not set in fastcgi_params file,
|
||||
# i.e. /etc/nginx/fastcgi_params or in the parent contexts,
|
||||
# please comment off following line:
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
|
||||
# Mitigate https://httpoxy.org/ vulnerabilities
|
||||
fastcgi_param HTTP_PROXY "";
|
||||
|
||||
#error_log /dev/null;
|
||||
fastcgi_connect_timeout 300s;
|
||||
fastcgi_read_timeout 300s;
|
||||
fastcgi_send_timeout 300s;
|
||||
fastcgi_buffer_size 32k;
|
||||
fastcgi_buffers 4 32k;
|
||||
fastcgi_busy_buffers_size 32k;
|
||||
fastcgi_temp_file_write_size 32k;
|
||||
client_body_timeout 10s;
|
||||
send_timeout 60s; # default, increase if experiencing a lot of timeouts.
|
||||
output_buffers 1 32k;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
|
||||
# Only throw it at PHP-FPM if file exists (prevents PHP exploits).
|
||||
fastcgi_pass 127.0.0.1:1026; # or: unix:/var/run/php-fpm.sock;
|
||||
# fastcgi_pass unix:/var/run/php8-fpm.sock;
|
||||
|
||||
# SCRIPT_FILENAME parameter is used for PHP FPM determining
|
||||
# the script name. If it is not set in fastcgi_params file,
|
||||
# i.e. /etc/nginx/fastcgi_params or in the parent contexts,
|
||||
# please comment off following line:
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=ssw
|
||||
PKG_VERSION:=0.0.2
|
||||
PKG_RELEASE:=1
|
||||
PKG_RELEASE:=2
|
||||
PKG_MAINTAINER:=Konstantine Shevlakov <shevlakov@132lan.ru>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
# SIM slot switcher for HuastLink HC-G60 or ZBT-WE2806-A
|
||||
# by Konstantine Shevlakov at <shevlakov@132lan.ru> 2025
|
||||
# Audited & fixed 2025
|
||||
|
||||
START=99
|
||||
STOP=10
|
||||
@ -11,7 +12,6 @@ load_config_gpio(){
|
||||
local gpio value
|
||||
for c in gpio value; do
|
||||
config_get $1_$c "$1" $c
|
||||
export $1_$c
|
||||
done
|
||||
}
|
||||
|
||||
@ -19,54 +19,58 @@ load_config_failover(){
|
||||
local enable mode interval times_rsrp times_ping revert rsrp host
|
||||
for c in enable mode interval times_rsrp times_ping revert rsrp host; do
|
||||
config_get $1_$c "$1" $c
|
||||
export $1_$c
|
||||
done
|
||||
}
|
||||
|
||||
reload_iface(){
|
||||
[ "$iface" ] && {
|
||||
[ "$iface" ] && {
|
||||
for i in $iface; do
|
||||
ifup $i
|
||||
ifup "$i"
|
||||
done
|
||||
}
|
||||
}
|
||||
|
||||
_apply_modem_state(){
|
||||
echo "$sim_value" > /sys/class/gpio/"$sim_gpio"/value
|
||||
echo "$modem_value" > /sys/class/gpio/"$modem_gpio"/value
|
||||
if [ -x /etc/init.d/smstools3 ]; then
|
||||
/etc/init.d/smstools3 restart
|
||||
fi
|
||||
sleep 30 && reload_iface &
|
||||
}
|
||||
|
||||
start_service(){
|
||||
config_load ssw
|
||||
config_foreach load_config_gpio modem
|
||||
config_foreach load_config_gpio sim
|
||||
modem_cur=$(cat /sys/class/gpio/$modem_gpio/value)
|
||||
sim_cur=$(cat /sys/class/gpio/$sim_gpio/value)
|
||||
iface=$(uci show network | awk -F [.] '/devices/{gsub("'\''","");print $2}' | tail -1)
|
||||
if [ "$modem_value" = "$modem_cur" -a "$sim_value" = "$sim_cur" ]; then
|
||||
continue
|
||||
elif [ "$modem_value" = "$modem_cur" -a "$modem_value" = "1" ]; then
|
||||
logger -t "SIM SW" "modem already enabled on gpio $modem_gpio. Restart it"
|
||||
echo "0" > /sys/class/gpio/$modem_gpio/value
|
||||
|
||||
local modem_cur sim_cur
|
||||
modem_cur=$(cat /sys/class/gpio/"$modem_gpio"/value)
|
||||
sim_cur=$(cat /sys/class/gpio/"$sim_gpio"/value)
|
||||
|
||||
iface=$(uci show network | awk -F'[.]' '/devices/{gsub("'\''",""); print $2}' | tail -1)
|
||||
|
||||
if [ "$modem_value" = "$modem_cur" ] && [ "$sim_value" = "$sim_cur" ]; then
|
||||
logger -t "SIM SW" "Modem and SIM already in correct state. Nothing to do."
|
||||
return 0
|
||||
elif [ "$modem_value" = "$modem_cur" ] && [ "$modem_value" = "1" ]; then
|
||||
logger -t "SIM SW" "Modem already enabled on gpio $modem_gpio. Restart it."
|
||||
echo "0" > /sys/class/gpio/"$modem_gpio"/value
|
||||
sleep 2
|
||||
echo "$sim_value" > /sys/class/gpio/$sim_gpio/value
|
||||
echo "$modem_value" > /sys/class/gpio/$modem_gpio/value
|
||||
if [ -x /etc/init.d/smstools3 ]; then
|
||||
/etc/init.d/smstools3 restart
|
||||
fi
|
||||
sleep 30 && reload_iface &
|
||||
_apply_modem_state
|
||||
else
|
||||
logger -t "SIM SW" "modem powered changed"
|
||||
echo "$sim_value" > /sys/class/gpio/$sim_gpio/value
|
||||
echo "$modem_value" > /sys/class/gpio/$modem_gpio/value
|
||||
if [ -x /etc/init.d/smstools3 ]; then
|
||||
/etc/init.d/smstools3 restart
|
||||
fi
|
||||
sleep 30 && reload_iface &
|
||||
logger -t "SIM SW" "Modem power state changed."
|
||||
_apply_modem_state
|
||||
fi
|
||||
#config_foreach load_config_failover failover
|
||||
|
||||
procd_open_instance
|
||||
procd_set_param command /usr/share/ssw_track.sh
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
|
||||
reload_service(){
|
||||
start
|
||||
start_service
|
||||
}
|
||||
|
||||
service_triggers(){
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
# SIM slot switcher for HuastLink HC-G60 or ZBT-WE2806-A
|
||||
# or similiar cpe OpenWrt-routers
|
||||
# or similar CPE OpenWrt-routers
|
||||
# by Konstantine Shevlakov at <shevlakov@132lan.ru> 2025
|
||||
# Audited & fixed 2026
|
||||
|
||||
NODE="SW SIM"
|
||||
|
||||
@ -34,38 +35,58 @@ NODE="SW SIM"
|
||||
# option value '1'
|
||||
|
||||
|
||||
# Get Variables
|
||||
. /lib/functions.sh
|
||||
|
||||
# Callback failover section
|
||||
_load_failover(){
|
||||
config_get enable "$1" enable ""
|
||||
config_get interval "$1" interval 60
|
||||
config_get revert "$1" revert ""
|
||||
config_get rsrp "$1" rsrp ""
|
||||
config_get times_rsrp "$1" times_rsrp 5
|
||||
config_get apn1 "$1" apn1 ""
|
||||
config_get apn2 "$1" apn2 ""
|
||||
}
|
||||
|
||||
# Callback sections modem / sim (gpio type)
|
||||
_load_gpio(){
|
||||
local _gpio _value
|
||||
config_get _gpio "$1" gpio ""
|
||||
config_get _value "$1" value ""
|
||||
eval "${1}_gpio=\"$_gpio\""
|
||||
eval "${1}_value=\"$_value\""
|
||||
}
|
||||
|
||||
# Get Variables — update for every cycle
|
||||
get_vars(){
|
||||
for v in enable interval revert rsrp times_rsrp apn1 apn2; do
|
||||
eval $v=$(uci -q get ssw.failover.${v} 2>/dev/nul)
|
||||
done
|
||||
for d in modem sim; do
|
||||
for s in gpio value; do
|
||||
eval "${d}_${s}=$(uci -q get ssw.${d}.${s} 2>/dev/null)"
|
||||
done
|
||||
done
|
||||
[ -n "$interval" ] || interval=60
|
||||
[ -n "$times_rsrp" ] || times_rsrp=5
|
||||
reset_cb
|
||||
config_load ssw
|
||||
config_foreach _load_failover failover
|
||||
config_foreach _load_gpio modem
|
||||
config_foreach _load_gpio sim
|
||||
}
|
||||
|
||||
# SIM Switch
|
||||
sw_sim(){
|
||||
case $cur_sim in
|
||||
case "$cur_sim" in
|
||||
0) next_sim=1 ;;
|
||||
1) next_sim=0 ;;
|
||||
*)
|
||||
logger -t "$NODE" "ERROR: cur_sim has unexpected value '$cur_sim'. Aborting sw_sim."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "0" > /sys/class/gpio/$modem_gpio/value
|
||||
echo "$next_sim" > /sys/class/gpio/$sim_gpio/value
|
||||
echo "0" > /sys/class/gpio/"$modem_gpio"/value
|
||||
echo "$next_sim" > /sys/class/gpio/"$sim_gpio"/value
|
||||
|
||||
if [ "$modem_value" = "1" ]; then
|
||||
sleep 2
|
||||
echo "1" > /sys/class/gpio/$modem_gpio/value
|
||||
echo "1" > /sys/class/gpio/"$modem_gpio"/value
|
||||
if [ -x /etc/init.d/smstools3 ]; then
|
||||
/etc/init.d/smstools3 restart
|
||||
fi
|
||||
fi
|
||||
set > /tmp/apn.ssw
|
||||
}
|
||||
|
||||
# Revert rule switch
|
||||
@ -84,32 +105,59 @@ sw_rule(){
|
||||
|
||||
# Check interface state via mwan3
|
||||
monitor_mwan3(){
|
||||
iface=$(uci show network | awk -F [.] '/devices/{print $2}')
|
||||
iface=$(uci show network | awk -F'[.]' '/devices/{print $2}' | tail -1)
|
||||
# Check link status
|
||||
if [ -r /tmp/run/mwan3/iface_state/$iface ]; then
|
||||
link_status=$(cat /tmp/run/mwan3/iface_state/$iface | grep online | wc -l)
|
||||
if [ -r "/tmp/run/mwan3/iface_state/$iface" ]; then
|
||||
link_status=$(grep -c online "/tmp/run/mwan3/iface_state/$iface" || echo 0)
|
||||
else
|
||||
# Disable track link via mwan3
|
||||
link_status=1
|
||||
fi
|
||||
}
|
||||
|
||||
# RSRP average value by modemmanager. Enable singnal monitor!
|
||||
check_signal(){
|
||||
local gen="$1" signal_json="$2"
|
||||
local metric value
|
||||
|
||||
case "$gen" in
|
||||
lte|5g) metric="rsrp" ;;
|
||||
3g) metric="rscp" ;;
|
||||
*)
|
||||
logger -t "$NODE" "check_signal: unknown generation '$gen'"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
value=$(echo "$signal_json" \
|
||||
| jsonfilter -e "@[\"modem\"][*][\"${gen}\"][\"${metric}\"]" 2>/dev/null \
|
||||
| awk '{printf "%.0f\n", $1}')
|
||||
|
||||
[ -n "$value" ] && echo "$value"
|
||||
}
|
||||
|
||||
# RSRP/RSCP average value by modemmanager. Enable signal monitor!
|
||||
monitor_rsrp(){
|
||||
device="$(uci show network | awk -F [=] '/devices/{gsub("'\''","");print $2}')"
|
||||
SIGNAL="$(mmcli -J -m $device --signal-get)"
|
||||
CRSRP=$(echo "$SIGNAL" | jsonfilter -e '@["modem"][*]["lte"]["rsrp"]' | awk '{printf "%.0f\n", $1}')
|
||||
if ! [ $CRSRP ]; then
|
||||
CRSRP=$(echo "$SIGNAL" | jsonfilter -e '@["modem"][*]["5g"]["rsrp"]' | awk '{printf "%.0f\n", $1}')
|
||||
local device SIGNAL CRSRP
|
||||
|
||||
device="$(uci show network | awk -F'[=]' '/devices/{gsub("'\''",""); print $2}' | tail -1)"
|
||||
SIGNAL="$(mmcli -J -m "$device" --signal-get 2>/dev/null)"
|
||||
|
||||
for gen in lte 5g 3g; do
|
||||
CRSRP=$(check_signal "$gen" "$SIGNAL")
|
||||
[ -n "$CRSRP" ] && break
|
||||
done
|
||||
|
||||
if [ -n "$CRSRP" ] && [ "$CRSRP" -ne 0 ]; then
|
||||
echo "$CRSRP" >> /tmp/ssw_rsrp.var
|
||||
fi
|
||||
|
||||
if [ $CRSRP -ne 0 ]; then
|
||||
echo $CRSRP >> /tmp/ssw_rsrp.var
|
||||
fi
|
||||
if [ $cnt -eq $times_rsrp ]; then
|
||||
RSRP=$(awk '{sum+=$1} END { printf "%.0f\n", sum/NR }' /tmp/ssw_rsrp.var)
|
||||
cat /dev/null > /tmp/ssw_rsrp.var
|
||||
if [ $RSRP -lt $rsrp ]; then
|
||||
if [ "$cnt" -eq "$times_rsrp" ]; then
|
||||
if [ -s /tmp/ssw_rsrp.var ]; then
|
||||
RSRP=$(awk '{sum+=$1} END { printf "%.0f\n", sum/NR }' /tmp/ssw_rsrp.var)
|
||||
fi
|
||||
: > /tmp/ssw_rsrp.var
|
||||
|
||||
if [ -n "$RSRP" ] && [ "$RSRP" -lt "$rsrp" ]; then
|
||||
mon_rsrp=0
|
||||
else
|
||||
mon_rsrp=1
|
||||
@ -121,68 +169,74 @@ monitor_rsrp(){
|
||||
reload_iface(){
|
||||
[ "$iface" ] && {
|
||||
for i in $iface; do
|
||||
ifup $i
|
||||
ifup "$i"
|
||||
done
|
||||
}
|
||||
}
|
||||
|
||||
# Stuff
|
||||
cnt=1
|
||||
cnt=0
|
||||
|
||||
while true; do
|
||||
get_vars
|
||||
sleep $interval
|
||||
sleep "$interval"
|
||||
|
||||
if [ "$enable" = "1" ]; then
|
||||
cur_sim=$(cat /sys/class/gpio/$sim_gpio/value)
|
||||
cur_sim=$(cat /sys/class/gpio/"$sim_gpio"/value)
|
||||
|
||||
cnt=$(( cnt + 1 ))
|
||||
monitor_rsrp
|
||||
monitor_mwan3
|
||||
|
||||
if [ "$cnt" -eq "$times_rsrp" ]; then
|
||||
if [ "$link_status" = "0" -o "$mon_rsrp" = "0" ]; then
|
||||
if [ "$link_status" = "0" ] || [ "$mon_rsrp" = "0" ]; then
|
||||
|
||||
if [ "$sim_value" -eq "$cur_sim" ]; then
|
||||
apn=$apn2
|
||||
else
|
||||
apn=$apn1
|
||||
fi
|
||||
iface=$(uci show network | awk -F [.] '/devices/{gsub("'\''","");print $2}' | tail -1)
|
||||
if [ $RSRP ]; then
|
||||
|
||||
iface=$(uci show network | awk -F'[.]' '/devices/{gsub("'\''",""); print $2}' | tail -1)
|
||||
|
||||
if [ -n "$RSRP" ]; then
|
||||
if [ "$mon_rsrp" = "0" ]; then
|
||||
logger -t "$NODE" "Modem interface: $iface is average RSRP= ${RSRP} dBm. Min. value ${rsrp} dBm."
|
||||
logger -t "$NODE" "Modem interface: $iface average RSRP=${RSRP} dBm. Min. value ${rsrp} dBm."
|
||||
fi
|
||||
if [ "$link_status" = "0" ]; then
|
||||
logger -t "$NODE" "Modem interface: $iface is loss connectivity"
|
||||
logger -t "$NODE" "Modem interface: $iface lost connectivity."
|
||||
fi
|
||||
else
|
||||
logger -t "$NODE" "WARNING: RSRP value not exist. Please check \"Resfesh signal\" option of modem interface!"
|
||||
logger -t "$NODE" "WARNING: RSRP value not available. Check 'Refresh signal' option of modem interface!"
|
||||
fi
|
||||
if [ "${#apn}" -gt "0" ]; then
|
||||
uci set network.$iface.apn="$apn"
|
||||
|
||||
if [ "${#apn}" -gt 0 ]; then
|
||||
uci set network."$iface".apn="$apn"
|
||||
logger -t "$NODE" "Switch SIM-card slot with APN: $apn"
|
||||
else
|
||||
logger -t "$NODE" "WARNING: APN not defined. Switch SIM-card slot with default APN."
|
||||
uci set network.$iface.apn="internet"
|
||||
logger -t "$NODE" "WARNING: APN not defined. Switching SIM-card slot with default APN."
|
||||
uci set network."$iface".apn="internet"
|
||||
fi
|
||||
|
||||
uci commit network
|
||||
reload_config network
|
||||
reload_config
|
||||
|
||||
if [ "$revert" = "1" ]; then
|
||||
if [ "$cur_sim" -eq "$sim_value" ]; then
|
||||
FBT=${FBT:=$((($interval+$times_rsrp)*2))}
|
||||
FBT=$(($FBT*2))
|
||||
SWDATE=$((`date +%s`+$FBT))
|
||||
echo "FBT=$FBT" > /tmp/ssw.vars
|
||||
echo SWDATE=$SWDATE >> /tmp/ssw.vars
|
||||
logger -t "$NODE" "Back to default SIM-slot after $(date -d @${SWDATE})"
|
||||
if [ "$cur_sim" -eq "$sim_value" ]; then
|
||||
FBT=${FBT:=$(( (interval + times_rsrp) * 2 ))}
|
||||
FBT=$(( FBT * 2 ))
|
||||
SWDATE=$(( $(date +%s) + FBT ))
|
||||
printf 'FBT=%s\nSWDATE=%s\n' "$FBT" "$SWDATE" > /tmp/ssw.vars
|
||||
logger -t "$NODE" "Back to default SIM-slot after $(date -d @"${SWDATE}")"
|
||||
fi
|
||||
fi
|
||||
|
||||
sw_sim && sleep 20 && reload_iface &
|
||||
fi
|
||||
if [ "$revert" = "1" ]; then
|
||||
|
||||
elif [ "$revert" = "1" ]; then
|
||||
sw_rule && sleep 20 && reload_iface &
|
||||
fi
|
||||
cnt=0
|
||||
else
|
||||
cnt=$(($cnt+1))
|
||||
|
||||
cnt=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done
|
||||
Loading…
Reference in New Issue
Block a user