mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-07-27 10:31:38 +08:00
🎨 Sync 2026-04-28 20:38:51
This commit is contained in:
parent
37afaea9db
commit
cabfade482
@ -6,8 +6,8 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gowebdav
|
||||
PKG_VERSION:=5.11.6
|
||||
PKG_RELEASE:=6
|
||||
PKG_VERSION:=5.11.7
|
||||
PKG_RELEASE:=7
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/hacdias/webdav/tar.gz/v$(PKG_VERSION)?
|
||||
|
||||
@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=hickory-dns
|
||||
PKG_VERSION:=0.26.0-beta.2
|
||||
PKG_RELEASE:=15
|
||||
PKG_RELEASE:=16
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/hickory-dns/hickory-dns/tar.gz/v$(PKG_VERSION)?
|
||||
|
||||
@ -2,6 +2,16 @@ listen_addrs_ipv6 = ["::1"]
|
||||
listen_port = 5335
|
||||
prometheus_listen_addr = "[::]:9000"
|
||||
|
||||
[[zones]]
|
||||
zone = "lan"
|
||||
zone_type = "Primary"
|
||||
|
||||
[zones.stores]
|
||||
type = "sqlite"
|
||||
zone_path = "/etc/hickory-dns/lan.zone"
|
||||
journal_path = "/var/lib/hickory-dns/lan.jrnl"
|
||||
allow_update = true
|
||||
|
||||
[[zones]]
|
||||
zone = "."
|
||||
zone_type = "External"
|
||||
|
||||
11
hickory-dns/files/etc/hickory-dns/lan.zone
Normal file
11
hickory-dns/files/etc/hickory-dns/lan.zone
Normal file
@ -0,0 +1,11 @@
|
||||
$TTL 60
|
||||
@ IN SOA ns.lan. admin.lan. (
|
||||
1 ; Serial
|
||||
3600 ; Refresh
|
||||
600 ; Retry
|
||||
86400 ; Expire
|
||||
60 ) ; Negative caching TTL
|
||||
|
||||
IN NS ns.lan.
|
||||
|
||||
ns IN AAAA ::1
|
||||
1493
ipv6-neigh/Cargo.lock
generated
Normal file
1493
ipv6-neigh/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
24
ipv6-neigh/Cargo.toml
Normal file
24
ipv6-neigh/Cargo.toml
Normal file
@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "ip-neigh"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
rtnetlink = { git = "https://github.com/rust-netlink/rtnetlink.git", rev = "515471f" }
|
||||
netlink-sys = { version = "0.8" }
|
||||
tokio = { version = "1.0.1", features = ["full"]}
|
||||
futures = "0.3.11"
|
||||
netlink-packet-route = {version = "0.21" }
|
||||
netlink-packet-core= {version ="0.7"}
|
||||
clap = { version = "4.0", default-features = false, features = ["cargo", "derive", "help", "std", "suggestions"] }
|
||||
hickory-proto = { git = "https://github.com/hickory-dns/hickory-dns.git", rev = "34a4cca1a707b642052b427938b7d1e8aaa71e3a" }
|
||||
ubus = "0.1.6"
|
||||
serde_json = "1.0.108"
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
strip = true
|
||||
51
ipv6-neigh/Makefile
Normal file
51
ipv6-neigh/Makefile
Normal file
@ -0,0 +1,51 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=ipv6-neigh
|
||||
PKG_VERSION:=0.1.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_BUILD_DEPENDS:=rust/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
|
||||
RUST_PKG_LOCKED:=0
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(TOPDIR)/feeds/packages/lang/rust/rust-package.mk
|
||||
|
||||
define Package/ipv6-neigh
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
SUBMENU:=IP Addresses and Names
|
||||
TITLE:=LAN neighbor DNS updater for hickory-dns
|
||||
URL:=https://github.com/nicebonn/hickory-dns-openwrt
|
||||
DEPENDS:=$(RUST_ARCH_DEPENDS) +hickory-dns
|
||||
endef
|
||||
|
||||
define Package/ipv6-neigh/description
|
||||
Monitors Linux neighbor table (ARP/NDP) via netlink and dynamically
|
||||
updates DNS records in a local hickory-dns server using RFC 2136
|
||||
DNS UPDATE over TCP. Maps MAC addresses to hostnames via OpenWrt
|
||||
DHCP leases (ubus).
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
+$(CARGO_PKG_VARS) \
|
||||
cargo build -v --profile $(CARGO_PKG_PROFILE) \
|
||||
--manifest-path "$(PKG_BUILD_DIR)/Cargo.toml" \
|
||||
--target $(RUSTC_TARGET_ARCH) \
|
||||
--no-default-features \
|
||||
$(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/
|
||||
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_DIR) $(1)/etc/init.d/
|
||||
$(INSTALL_BIN) ./files/etc/init.d/ipv6-neigh $(1)/etc/init.d/ipv6-neigh
|
||||
endef
|
||||
|
||||
$(eval $(call RustBinPackage,ipv6-neigh))
|
||||
$(eval $(call BuildPackage,ipv6-neigh))
|
||||
20
ipv6-neigh/files/etc/init.d/ipv6-neigh
Normal file
20
ipv6-neigh/files/etc/init.d/ipv6-neigh
Normal file
@ -0,0 +1,20 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
USE_PROCD=1
|
||||
START=19
|
||||
STOP=50
|
||||
|
||||
PROG=/usr/bin/ipv6-neigh
|
||||
|
||||
start_service() {
|
||||
procd_open_instance ipv6-neigh
|
||||
procd_set_param command $PROG -p -d "[::1]:5335" -z lan
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
procd_set_param respawn "${respawn_threshold:-3600}" "${respawn_timeout:-5}" "${respawn_retry:-5}"
|
||||
procd_close_instance ipv6-neigh
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger "dhcp" "network"
|
||||
}
|
||||
107
ipv6-neigh/src/db.rs
Normal file
107
ipv6-neigh/src/db.rs
Normal file
@ -0,0 +1,107 @@
|
||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
|
||||
use hickory_proto::op::{Message, ResponseCode, update_message};
|
||||
use hickory_proto::rr::{Name, RData, RecordSet, RecordType};
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
/// DNS dynamic update client that sends RFC 2136 updates over TCP to the hickory-dns server.
|
||||
pub(crate) struct DnsUpdater {
|
||||
server_addr: SocketAddr,
|
||||
zone: Name,
|
||||
}
|
||||
|
||||
impl DnsUpdater {
|
||||
pub fn new(server_addr: SocketAddr, zone: Name) -> Self {
|
||||
Self { server_addr, zone }
|
||||
}
|
||||
|
||||
/// Create or append a AAAA record.
|
||||
pub async fn upsert_aaaa(
|
||||
&self,
|
||||
hostname: &str,
|
||||
addr: Ipv6Addr,
|
||||
ttl: u32,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let name = Name::from_ascii(hostname)?.append_domain(&self.zone)?;
|
||||
let mut rrset = RecordSet::with_ttl(name.clone(), RecordType::AAAA, ttl);
|
||||
rrset.add_rdata(RData::AAAA(addr.into()));
|
||||
|
||||
let msg = update_message::create(rrset, self.zone.clone(), false);
|
||||
let response = self.send_tcp(msg).await?;
|
||||
if response.metadata.response_code == ResponseCode::YXRRSet {
|
||||
let mut rrset = RecordSet::with_ttl(name, RecordType::AAAA, ttl);
|
||||
rrset.add_rdata(RData::AAAA(addr.into()));
|
||||
let msg = update_message::append(rrset, self.zone.clone(), false, false);
|
||||
self.send_tcp(msg).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create or append an A record.
|
||||
pub async fn upsert_a(
|
||||
&self,
|
||||
hostname: &str,
|
||||
addr: Ipv4Addr,
|
||||
ttl: u32,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let name = Name::from_ascii(hostname)?.append_domain(&self.zone)?;
|
||||
let mut rrset = RecordSet::with_ttl(name.clone(), RecordType::A, ttl);
|
||||
rrset.add_rdata(RData::A(addr.into()));
|
||||
|
||||
let msg = update_message::create(rrset, self.zone.clone(), false);
|
||||
let response = self.send_tcp(msg).await?;
|
||||
if response.metadata.response_code == ResponseCode::YXRRSet {
|
||||
let mut rrset = RecordSet::with_ttl(name, RecordType::A, ttl);
|
||||
rrset.add_rdata(RData::A(addr.into()));
|
||||
let msg = update_message::append(rrset, self.zone.clone(), false, false);
|
||||
self.send_tcp(msg).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete a specific AAAA record.
|
||||
pub async fn delete_aaaa(
|
||||
&self,
|
||||
hostname: &str,
|
||||
addr: Ipv6Addr,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let name = Name::from_ascii(hostname)?.append_domain(&self.zone)?;
|
||||
let mut rrset = RecordSet::new(name, RecordType::AAAA, 0);
|
||||
rrset.add_rdata(RData::AAAA(addr.into()));
|
||||
let msg = update_message::delete_by_rdata(rrset, self.zone.clone(), false);
|
||||
self.send_tcp(msg).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete a specific A record.
|
||||
pub async fn delete_a(
|
||||
&self,
|
||||
hostname: &str,
|
||||
addr: Ipv4Addr,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let name = Name::from_ascii(hostname)?.append_domain(&self.zone)?;
|
||||
let mut rrset = RecordSet::new(name, RecordType::A, 0);
|
||||
rrset.add_rdata(RData::A(addr.into()));
|
||||
let msg = update_message::delete_by_rdata(rrset, self.zone.clone(), false);
|
||||
self.send_tcp(msg).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send a DNS message over TCP (2-byte length prefix + message bytes) and read the response.
|
||||
async fn send_tcp(&self, msg: Message) -> Result<Message, Box<dyn std::error::Error>> {
|
||||
let bytes = msg.to_vec()?;
|
||||
let len = u16::try_from(bytes.len())?;
|
||||
|
||||
let mut stream = TcpStream::connect(self.server_addr).await?;
|
||||
stream.write_all(&len.to_be_bytes()).await?;
|
||||
stream.write_all(&bytes).await?;
|
||||
stream.flush().await?;
|
||||
|
||||
let resp_len = stream.read_u16().await? as usize;
|
||||
let mut resp_buf = vec![0u8; resp_len];
|
||||
stream.read_exact(&mut resp_buf).await?;
|
||||
|
||||
Ok(Message::from_vec(&resp_buf)?)
|
||||
}
|
||||
}
|
||||
298
ipv6-neigh/src/main.rs
Normal file
298
ipv6-neigh/src/main.rs
Normal file
@ -0,0 +1,298 @@
|
||||
use std::collections::HashMap;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr as StdSocketAddr};
|
||||
|
||||
use futures::stream::StreamExt;
|
||||
use futures::stream::TryStreamExt;
|
||||
use netlink_packet_core::NetlinkPayload;
|
||||
use netlink_packet_route::RouteNetlinkMessage;
|
||||
use netlink_packet_route::neighbour::NeighbourMessage;
|
||||
use netlink_packet_route::neighbour::{NeighbourAddress, NeighbourAttribute, NeighbourState};
|
||||
use netlink_packet_route::route::RouteType;
|
||||
use rtnetlink::{Error, Handle, new_connection};
|
||||
|
||||
use clap::Parser;
|
||||
use hickory_proto::rr::Name;
|
||||
use netlink_sys::{AsyncSocket, SocketAddr};
|
||||
mod db;
|
||||
mod op;
|
||||
const RTNLGRP_NEIGH: u32 = 3;
|
||||
|
||||
const DEFAULT_TTL: u32 = 60;
|
||||
|
||||
const fn nl_mgrp(group: u32) -> u32 {
|
||||
if group > 31 {
|
||||
panic!("use netlink_sys::Socket::add_membership() for this group");
|
||||
}
|
||||
if group == 0 { 0 } else { 1 << (group - 1) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[clap()]
|
||||
struct Cli {
|
||||
#[clap(short, long)]
|
||||
iface: Option<String>,
|
||||
#[clap(short, long)]
|
||||
private_subnet: bool,
|
||||
/// hickory-dns server address for DNS updates (e.g. "[::1]:5335")
|
||||
#[clap(short, long, default_value = "[::1]:5335")]
|
||||
dns_server: StdSocketAddr,
|
||||
/// DNS zone to update (e.g. "lan")
|
||||
#[clap(short, long, default_value = "lan")]
|
||||
zone: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Neigh {
|
||||
ifindex: u32,
|
||||
state: NeighbourState,
|
||||
kind: RouteType,
|
||||
inet: NeighbourAddress,
|
||||
mac: String,
|
||||
}
|
||||
|
||||
fn if_ipv6_in_private_subnet(ip: &Ipv6Addr) -> bool {
|
||||
// Check if address is ULA (fc00::/7)
|
||||
let is_ula = (ip.segments()[0] & 0xfe00) == 0xfc00;
|
||||
|
||||
// Check if address is link-local (fe80::/10)
|
||||
let is_link_local = (ip.segments()[0] & 0xffc0) == 0xfe80;
|
||||
|
||||
is_ula || is_link_local
|
||||
}
|
||||
|
||||
fn if_ipv4_in_private_subnet(ip: &Ipv4Addr) -> bool {
|
||||
// Check for private network ranges
|
||||
let octets = ip.octets();
|
||||
|
||||
// 10.0.0.0/8
|
||||
if octets[0] == 10 {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 172.16.0.0/12
|
||||
if octets[0] == 172 && (octets[1] >= 16 && octets[1] <= 31) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 192.168.0.0/16
|
||||
if octets[0] == 192 && octets[1] == 168 {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 127.0.0.0/8 (loopback)
|
||||
if octets[0] == 127 {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
async fn process_new_neigh(neigh: &Neigh, updater: &db::DnsUpdater, leases: &HashMap<String, String>) {
|
||||
let Some(hostname) = leases.get(&neigh.mac) else {
|
||||
eprintln!("no lease for mac {}, skipping DNS update", neigh.mac);
|
||||
return;
|
||||
};
|
||||
let result = match &neigh.inet {
|
||||
NeighbourAddress::Inet6(addr) => updater.upsert_aaaa(hostname, *addr, DEFAULT_TTL).await,
|
||||
NeighbourAddress::Inet(addr) => updater.upsert_a(hostname, *addr, DEFAULT_TTL).await,
|
||||
_ => return,
|
||||
};
|
||||
match result {
|
||||
Ok(()) => println!("DNS update: added {} -> {:?}", hostname, neigh.inet),
|
||||
Err(e) => eprintln!("DNS update failed for {}: {}", hostname, e),
|
||||
}
|
||||
}
|
||||
|
||||
async fn process_del_neigh(neigh: &Neigh, updater: &db::DnsUpdater, leases: &HashMap<String, String>) {
|
||||
let Some(hostname) = leases.get(&neigh.mac) else {
|
||||
return;
|
||||
};
|
||||
let result = match &neigh.inet {
|
||||
NeighbourAddress::Inet6(addr) => updater.delete_aaaa(hostname, *addr).await,
|
||||
NeighbourAddress::Inet(addr) => updater.delete_a(hostname, *addr).await,
|
||||
_ => return,
|
||||
};
|
||||
match result {
|
||||
Ok(()) => println!("DNS update: removed {} -> {:?}", hostname, neigh.inet),
|
||||
Err(e) => eprintln!("DNS delete failed for {}: {}", hostname, e),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_multicast_or_broadcast_route_type(route_type: RouteType) -> bool {
|
||||
match route_type {
|
||||
RouteType::Multicast => true,
|
||||
RouteType::Broadcast => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_multicast_or_broadcast(neigh: &Neigh) -> bool {
|
||||
return is_multicast_or_broadcast_route_type(neigh.kind);
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), ()> {
|
||||
let args = Cli::parse();
|
||||
let private_subnet = args.private_subnet;
|
||||
let zone = Name::from_ascii(&args.zone).expect("invalid zone name");
|
||||
let updater = db::DnsUpdater::new(args.dns_server, zone);
|
||||
|
||||
let (connection, handle, _) = new_connection().unwrap();
|
||||
tokio::spawn(connection);
|
||||
dump_addresses(handle.clone(), args.iface).await.unwrap();
|
||||
|
||||
// Load DHCP leases (mac -> hostname) from ubus
|
||||
let mut leases = op::get_lease().unwrap_or_default();
|
||||
println!("loaded {} DHCP leases", leases.len());
|
||||
|
||||
// Dump existing neighbours and register them
|
||||
println!("dumping neighbours");
|
||||
if let Ok(neighbours) = dump_neighbours(handle.clone(), private_subnet).await {
|
||||
for neigh in &neighbours {
|
||||
println!("{:?}", neigh);
|
||||
if !is_multicast_or_broadcast(neigh) {
|
||||
process_new_neigh(neigh, &updater, &leases).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
println!();
|
||||
|
||||
let (mut conn, mut _handle, mut messages) =
|
||||
new_connection().map_err(|e| format!("{e}")).unwrap();
|
||||
|
||||
let groups = nl_mgrp(RTNLGRP_NEIGH);
|
||||
|
||||
let addr = SocketAddr::new(0, groups);
|
||||
conn.socket_mut()
|
||||
.socket_mut()
|
||||
.bind(&addr)
|
||||
.expect("Failed to bind");
|
||||
|
||||
tokio::spawn(conn);
|
||||
|
||||
let mut event_count: u64 = 0;
|
||||
|
||||
// Start receiving events through `messages` channel.
|
||||
while let Some((message, _)) = messages.next().await {
|
||||
// Refresh leases periodically (every 50 events)
|
||||
event_count += 1;
|
||||
if event_count % 50 == 0 {
|
||||
if let Ok(new_leases) = op::get_lease() {
|
||||
leases = new_leases;
|
||||
println!("refreshed {} DHCP leases", leases.len());
|
||||
}
|
||||
}
|
||||
|
||||
let payload = message.payload;
|
||||
if let NetlinkPayload::InnerMessage(msg) = payload {
|
||||
match msg {
|
||||
RouteNetlinkMessage::NewNeighbour(new_neigh) => {
|
||||
let Some(neigh) = parse_neighbour_message(new_neigh, private_subnet) else {
|
||||
continue;
|
||||
};
|
||||
if is_multicast_or_broadcast(&neigh) {
|
||||
continue;
|
||||
}
|
||||
println!("New neighbour: {:?}", neigh);
|
||||
process_new_neigh(&neigh, &updater, &leases).await;
|
||||
}
|
||||
RouteNetlinkMessage::DelNeighbour(del_neigh) => {
|
||||
let Some(neigh) = parse_neighbour_message(del_neigh, private_subnet) else {
|
||||
continue;
|
||||
};
|
||||
if is_multicast_or_broadcast(&neigh) {
|
||||
continue;
|
||||
}
|
||||
println!("Del neighbour: {:?}", neigh);
|
||||
process_del_neigh(&neigh, &updater, &leases).await;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format_mac(mac: Vec<u8>) -> String {
|
||||
let mut mac_str = String::new();
|
||||
for byte in mac {
|
||||
mac_str.push_str(&format!("{:02x}:", byte));
|
||||
}
|
||||
mac_str.pop();
|
||||
mac_str
|
||||
}
|
||||
|
||||
async fn dump_addresses(handle: Handle, link: Option<String>) -> Result<(), Error> {
|
||||
let mut request = handle.link().get();
|
||||
if let Some(link) = link {
|
||||
request = request.match_name(link);
|
||||
}
|
||||
|
||||
let mut links = request.execute();
|
||||
if let Some(link) = links.try_next().await? {
|
||||
let mut addresses = handle
|
||||
.address()
|
||||
.get()
|
||||
.set_link_index_filter(link.header.index)
|
||||
.execute();
|
||||
while let Some(msg) = addresses.try_next().await? {
|
||||
println!("{msg:?}");
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
eprintln!("link not found");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_neighbour_message(neigh: NeighbourMessage, private_subnet: bool) -> Option<Neigh> {
|
||||
let state = neigh.header.state;
|
||||
if state == NeighbourState::Permanent {
|
||||
return None;
|
||||
}
|
||||
let addr: NeighbourAddress = neigh.attributes.iter().find_map(|attr| match attr {
|
||||
NeighbourAttribute::Destination(inet) => Some(inet.to_owned()),
|
||||
_ => None,
|
||||
})?;
|
||||
if private_subnet {
|
||||
match addr {
|
||||
NeighbourAddress::Inet(addr) => {
|
||||
if !if_ipv4_in_private_subnet(&addr) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
NeighbourAddress::Inet6(addr) => {
|
||||
if !if_ipv6_in_private_subnet(&addr) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
let kind = neigh.header.kind;
|
||||
let ifindex = neigh.header.ifindex;
|
||||
let mac = neigh.attributes.iter().find_map(|attr| match attr {
|
||||
NeighbourAttribute::LinkLocalAddress(mac) => Some(mac.to_owned()),
|
||||
_ => None,
|
||||
})?;
|
||||
Some(Neigh {
|
||||
ifindex,
|
||||
state,
|
||||
kind,
|
||||
inet: addr,
|
||||
mac: format_mac(mac),
|
||||
})
|
||||
}
|
||||
|
||||
async fn dump_neighbours(handle: Handle, private_subnet: bool) -> Result<Vec<Neigh>, Error> {
|
||||
let mut neighbours = handle.neighbours().get().execute();
|
||||
let mut vec: Vec<Neigh> = Vec::new();
|
||||
while let Some(route) = neighbours.try_next().await? {
|
||||
if let Some(neigh) = parse_neighbour_message(route, private_subnet) {
|
||||
if !is_multicast_or_broadcast(&neigh) {
|
||||
vec.push(neigh);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(vec)
|
||||
}
|
||||
39
ipv6-neigh/src/op.rs
Normal file
39
ipv6-neigh/src/op.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
pub fn call_ubus(obj_path: &str, method: &str) -> Result<Value, Box<dyn std::error::Error>> {
|
||||
let socket = Path::new("/var/run/ubus/ubus.sock");
|
||||
|
||||
let mut connection = match ubus::Connection::connect(&socket) {
|
||||
Ok(connection) => connection,
|
||||
Err(err) => {
|
||||
return Err(Box::new(err));
|
||||
}
|
||||
};
|
||||
|
||||
let json = connection.call(obj_path, method, "").unwrap();
|
||||
let parsed: Value = serde_json::from_str(&json).unwrap();
|
||||
Ok(parsed)
|
||||
}
|
||||
|
||||
// get mac to hostname mapping
|
||||
pub fn get_lease() -> Result<HashMap<String, String>, Box<dyn std::error::Error>> {
|
||||
// dhcpv6 does not use mac address, so we only need to get ipv4 leases
|
||||
let ip4lease = call_ubus("dhcp", "ipv4leases");
|
||||
let leases = ip4lease.unwrap();
|
||||
let devices = leases["device"].as_object().unwrap();
|
||||
let mut result = HashMap::new();
|
||||
// hard code
|
||||
for (device, leases) in devices {
|
||||
let suffix = if device == "phy1-ap0" { ".iot" } else { ".lan" };
|
||||
let leases = leases["leases"].as_array().unwrap();
|
||||
for lease in leases {
|
||||
let mac = lease["mac"].as_str().unwrap().to_string();
|
||||
let hostname = lease["hostname"].as_str().unwrap().to_string() + suffix;
|
||||
result.insert(mac, hostname);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
@ -16,8 +16,8 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-amlogic
|
||||
PKG_VERSION:=3.1.289
|
||||
PKG_RELEASE:=4
|
||||
PKG_VERSION:=3.1.290
|
||||
PKG_RELEASE:=5
|
||||
|
||||
PKG_LICENSE:=GPL-2.0 License
|
||||
PKG_MAINTAINER:=ophub <https://github.com/ophub/luci-app-amlogic>
|
||||
|
||||
@ -283,8 +283,8 @@ update_kernel() {
|
||||
rm -f /boot/uInitrd*
|
||||
else
|
||||
valid_files="vmlinuz-${kernel_name} uInitrd-${kernel_name} config-${kernel_name} System.map-${kernel_name}"
|
||||
# wxy-oect: MODEL_ID numbers r304,r306 and w[0-9]{3}, require special handling of uInitrd
|
||||
[[ "${MODEL_ID}" =~ ^(r304|r306|w[0-9]{3})$ ]] || rm -f /boot/initrd.img*
|
||||
# wxy-oect: MODEL_ID numbers r306,r307 and w[0-9]{3}, require special handling of uInitrd
|
||||
[[ "${MODEL_ID}" =~ ^(r306|r307|w[0-9]{3})$ ]] || rm -f /boot/initrd.img*
|
||||
fi
|
||||
for f in ${valid_files}; do [[ -f "/boot/${f}" ]] || restore_kernel; done
|
||||
|
||||
@ -304,8 +304,8 @@ update_kernel() {
|
||||
ln -sf vmlinuz-${kernel_name} ${MYBOOT_VMLINUZ}
|
||||
fi
|
||||
|
||||
# wxy-oect: MODEL_ID numbers r304 and r306, require special handling of uInitrd
|
||||
[[ "${MODEL_ID}" =~ ^(r304|r306)$ ]] && ln -sf initrd.img-${kernel_name} uInitrd
|
||||
# wxy-oect: MODEL_ID numbers r306 and r307, require special handling of uInitrd
|
||||
[[ "${MODEL_ID}" =~ ^(r306|r307)$ ]] && ln -sf initrd.img-${kernel_name} uInitrd
|
||||
)
|
||||
|
||||
echo -e "(1/3) Unpacking [ boot-${kernel_name}.tar.gz ] done."
|
||||
|
||||
@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-passwall
|
||||
PKG_VERSION:=26.4.15
|
||||
PKG_RELEASE:=100
|
||||
PKG_RELEASE:=101
|
||||
PKG_PO_VERSION:=$(PKG_VERSION)
|
||||
|
||||
PKG_CONFIG_DEPENDS:= \
|
||||
|
||||
@ -401,13 +401,6 @@ o.validate = function(self, value)
|
||||
return api.trim(value:gsub("[\r\n]", ""))
|
||||
end
|
||||
|
||||
o = s:option(ListValue, _n("ech_ForceQuery"), translate("ECH Query Policy"), translate("Controls the policy used when performing DNS queries for ECH configuration."))
|
||||
o.default = "full"
|
||||
o:value("none")
|
||||
o:value("half")
|
||||
o:value("full")
|
||||
o:depends({ [_n("ech")] = true })
|
||||
|
||||
-- [[ REALITY部分 ]] --
|
||||
o = s:option(Value, _n("reality_publicKey"), translate("Public Key"))
|
||||
o:depends({ [_n("tls")] = true, [_n("reality")] = true })
|
||||
|
||||
@ -163,8 +163,7 @@ function gen_outbound(flag, node, tag, proxy_table)
|
||||
if not node.tls_CertByName then return "" end
|
||||
return node.tls_CertByName
|
||||
end)(),
|
||||
echConfigList = (node.ech == "1") and node.ech_config or nil,
|
||||
echForceQuery = (node.ech == "1") and (node.ech_ForceQuery or "full") or nil
|
||||
echConfigList = (node.ech == "1") and node.ech_config or nil
|
||||
} or nil,
|
||||
realitySettings = (node.stream_security == "reality") and {
|
||||
serverName = node.tls_serverName,
|
||||
|
||||
@ -1909,12 +1909,6 @@ msgstr "ECH 配置"
|
||||
msgid "ECH Key"
|
||||
msgstr "ECH 密钥"
|
||||
|
||||
msgid "ECH Query Policy"
|
||||
msgstr "ECH 查询策略"
|
||||
|
||||
msgid "Controls the policy used when performing DNS queries for ECH configuration."
|
||||
msgstr "控制使用 DNS 查询 ECH 配置时的策略。"
|
||||
|
||||
msgid "ECH Query Domain"
|
||||
msgstr "ECH 查询域名"
|
||||
|
||||
|
||||
@ -939,13 +939,8 @@ add_firewall_rule() {
|
||||
fi
|
||||
}
|
||||
|
||||
ipset -! -R <<-EOF
|
||||
$(ip address show | grep -w "inet" | awk '{print $2}' | awk -F '/' '{print $1}' | sed -e "s/^/add $IPSET_LOCAL /")
|
||||
EOF
|
||||
|
||||
ipset -! -R <<-EOF
|
||||
$(ip address show | grep -w "inet6" | awk '{print $2}' | awk -F '/' '{print $1}' | sed -e "s/^/add $IPSET_LOCAL6 /")
|
||||
EOF
|
||||
get_local_ips ip4 | sed "s/^/add $IPSET_LOCAL /" | ipset -! -R
|
||||
get_local_ips ip6 | sed "s/^/add $IPSET_LOCAL6 /" | ipset -! -R
|
||||
|
||||
#局域网IP列表
|
||||
ipset -! -R <<-EOF
|
||||
@ -1023,9 +1018,9 @@ add_firewall_rule() {
|
||||
$ipt_n -N PSW_DNS
|
||||
if [ $(config_t_get global dns_redirect "1") = "0" ]; then
|
||||
#Only hijack when dest address is local IP
|
||||
$ipt_n -I PREROUTING $(dst $IPSET_LOCAL) -j PSW_DNS
|
||||
$ipt_n -I PREROUTING -m set --match-set $IPSET_LAN src $(dst $IPSET_LOCAL) -j PSW_DNS
|
||||
else
|
||||
$ipt_n -I PREROUTING 1 -j PSW_DNS
|
||||
$ipt_n -I PREROUTING -m set --match-set $IPSET_LAN src -j PSW_DNS
|
||||
fi
|
||||
|
||||
$ipt_m -N PSW_DIVERT
|
||||
@ -1094,9 +1089,9 @@ add_firewall_rule() {
|
||||
$ip6t_n -N PSW_DNS
|
||||
if [ $(config_t_get global dns_redirect "1") = "0" ]; then
|
||||
#Only hijack when dest address is local IP
|
||||
$ip6t_n -I PREROUTING $(dst $IPSET_LOCAL6) -j PSW_DNS
|
||||
$ip6t_n -I PREROUTING -m set --match-set $IPSET_LAN6 src $(dst $IPSET_LOCAL6) -j PSW_DNS
|
||||
else
|
||||
$ip6t_n -I PREROUTING 1 -j PSW_DNS
|
||||
$ip6t_n -I PREROUTING -m set --match-set $IPSET_LAN6 src -j PSW_DNS
|
||||
fi
|
||||
|
||||
$ip6t_m -N PSW_DIVERT
|
||||
|
||||
@ -1011,8 +1011,8 @@ add_firewall_rule() {
|
||||
fi
|
||||
}
|
||||
|
||||
ip address show | grep -w "inet" | awk '{print $2}' | awk -F '/' '{print $1}' | insert_nftset $NFTSET_LOCAL "-1"
|
||||
ip address show | grep -w "inet6" | awk '{print $2}' | awk -F '/' '{print $1}' | insert_nftset $NFTSET_LOCAL6 "-1"
|
||||
get_local_ips ip4 | insert_nftset $NFTSET_LOCAL "-1"
|
||||
get_local_ips ip6 | insert_nftset $NFTSET_LOCAL6 "-1"
|
||||
|
||||
# 忽略特殊IP段
|
||||
local lan_ifname lan_ip
|
||||
@ -1074,10 +1074,11 @@ add_firewall_rule() {
|
||||
nft "flush chain $NFTABLE_NAME PSW_DNS"
|
||||
if [ $(config_t_get global dns_redirect "1") = "0" ]; then
|
||||
#Only hijack when dest address is local IP
|
||||
nft "insert rule $NFTABLE_NAME dstnat ip daddr @${NFTSET_LOCAL} jump PSW_DNS"
|
||||
nft "insert rule $NFTABLE_NAME dstnat ip6 daddr @${NFTSET_LOCAL6} jump PSW_DNS"
|
||||
nft "insert rule $NFTABLE_NAME dstnat ip saddr @${NFTSET_LAN} ip daddr @${NFTSET_LOCAL} jump PSW_DNS"
|
||||
nft "insert rule $NFTABLE_NAME dstnat ip6 saddr @${NFTSET_LAN6} ip6 daddr @${NFTSET_LOCAL6} jump PSW_DNS"
|
||||
else
|
||||
nft "insert rule $NFTABLE_NAME dstnat jump PSW_DNS"
|
||||
nft "insert rule $NFTABLE_NAME dstnat ip saddr @${NFTSET_LAN} jump PSW_DNS"
|
||||
nft "insert rule $NFTABLE_NAME dstnat ip6 saddr @${NFTSET_LAN6} jump PSW_DNS"
|
||||
fi
|
||||
|
||||
# for ipv4 ipv6 tproxy mark
|
||||
|
||||
@ -518,3 +518,33 @@ get_wan_ips() {
|
||||
done
|
||||
echo "$NET_ADDR"
|
||||
}
|
||||
|
||||
get_local_ips() {
|
||||
local family="$1"
|
||||
local ALL_IPS WAN_IPS ip NET_ADDR
|
||||
if [ "$family" = "ip6" ]; then
|
||||
ALL_IPS=$(ip -o -6 addr show scope global | awk '{print $4}' | cut -d/ -f1)
|
||||
WAN_IPS=$(get_wan_ips ip6)
|
||||
else
|
||||
ALL_IPS=$(ip -o -4 addr show scope global | awk '{print $4}' | cut -d/ -f1)
|
||||
WAN_IPS=$(get_wan_ips ip4)
|
||||
fi
|
||||
# 补充回环(scope global 不包含)
|
||||
[ "$family" = "ip6" ] && ALL_IPS="$ALL_IPS ::1"
|
||||
[ "$family" != "ip6" ] && ALL_IPS="$ALL_IPS 127.0.0.1"
|
||||
for ip in $ALL_IPS; do
|
||||
case "$ip" in
|
||||
""|0.0.0.0|::) continue ;;
|
||||
esac
|
||||
case " $WAN_IPS " in
|
||||
*" $ip "*) continue ;;
|
||||
esac
|
||||
case " $NET_ADDR " in
|
||||
*" $ip "*) ;;
|
||||
*) NET_ADDR="${NET_ADDR:+$NET_ADDR }$ip" ;;
|
||||
esac
|
||||
done
|
||||
for ip in $NET_ADDR; do
|
||||
echo "$ip"
|
||||
done
|
||||
}
|
||||
|
||||
@ -46,6 +46,16 @@ function pushUnique(list, value) {
|
||||
list.push(value);
|
||||
}
|
||||
|
||||
function mergeUniqueValues(...lists) {
|
||||
const merged = [];
|
||||
|
||||
for (const list of lists)
|
||||
for (const value of L.toArray(list))
|
||||
pushUnique(merged, value);
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
function getQcaFallbackIfname(device, section) {
|
||||
if (/^ath\d+$/.test(String(section || '')))
|
||||
return section;
|
||||
@ -1541,7 +1551,9 @@ var CBIWifiFrequencyValue = form.Value.extend({
|
||||
const statuscfg = L.isObject(wstatus[device_section]?.config) ? wstatus[device_section].config : {};
|
||||
const devcfg = {
|
||||
channel: statuscfg.channel ?? (wifidevs ? wifidevs.ubus('dev', 'config', 'channel') : null),
|
||||
band: statuscfg.band ?? (wifidevs ? wifidevs.ubus('dev', 'config', 'band') : null)
|
||||
band: statuscfg.band ?? (wifidevs ? wifidevs.ubus('dev', 'config', 'band') : null),
|
||||
hwmode: statuscfg.hwmode ?? (wifidevs ? wifidevs.ubus('dev', 'config', 'hwmode') : null),
|
||||
htmode: statuscfg.htmode ?? (wifidevs ? wifidevs.ubus('dev', 'config', 'htmode') : null)
|
||||
};
|
||||
|
||||
this.devinfo = devinfo;
|
||||
@ -1592,8 +1604,19 @@ var CBIWifiFrequencyValue = form.Value.extend({
|
||||
return Array.isArray(channels) && (channels.length > offset || (configured_band == band && channels.length > 0));
|
||||
};
|
||||
|
||||
let hwmode_values = L.toArray(wifidevs ? wifidevs.getHWModes() : null);
|
||||
let htmode_values = L.toArray(wifidevs ? wifidevs.getHTModes() : null);
|
||||
const merge_mode_sources = (hwtype == 'mac80211');
|
||||
let hwmode_values = merge_mode_sources
|
||||
? mergeUniqueValues(
|
||||
wifidevs ? wifidevs.getHWModes() : null,
|
||||
devinfo.hwmodes
|
||||
)
|
||||
: L.toArray(wifidevs ? wifidevs.getHWModes() : null);
|
||||
let htmode_values = merge_mode_sources
|
||||
? mergeUniqueValues(
|
||||
wifidevs ? wifidevs.getHTModes() : null,
|
||||
devinfo.htmodes
|
||||
)
|
||||
: L.toArray(wifidevs ? wifidevs.getHTModes() : null);
|
||||
|
||||
if (!hwmode_values.length)
|
||||
hwmode_values = L.toArray(devinfo.hwmodes);
|
||||
@ -1959,8 +1982,8 @@ var CBIWifiFrequencyValue = form.Value.extend({
|
||||
const config_chval = cfgvals ? cfgvals[2] : uci.get('wireless', config_section, 'channel');
|
||||
const cfg_chval = devcfg.channel || config_chval;
|
||||
const cfg_bandval = devcfg.band || uci.get('wireless', config_section, 'band');
|
||||
const htval = isQcaWifiHwtype(hwtype) ? (cfg_htval || devinfo.htmode) : (devinfo.htmode || cfg_htval);
|
||||
const hwval = isQcaWifiHwtype(hwtype) ? (cfg_hwval || devinfo.hwmode) : (devinfo.hwmode || cfg_hwval);
|
||||
const htval = cfg_htval || devcfg.htmode || devinfo.htmode;
|
||||
const hwval = cfg_hwval || devcfg.hwmode || devinfo.hwmode;
|
||||
const chval = cfg_chval || devinfo.channel;
|
||||
const bandval = cfg_bandval || getConfiguredBand(hwtype, hwval, chval, null, htval);
|
||||
const forceSelectValue = function(sel, value) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user