🌈 Sync 2026-10-01 13:09:37

This commit is contained in:
github-actions[bot] committed 2026-10-01 13:09:37 +08:00
1 parent dceb6de0e4
commit 889951b6d0
116 files changed
+6376 -1093

No files matched your search

+3
View File
@@ -35,6 +35,7 @@ load_config() {
[ -n "$gateway" ] || network_get_gateway gateway "$interface"
}
# JSON array of the LAN neighbour table, marking which MACs are selected.
cmd_clients() {
load_config
local sel
@@ -52,6 +53,7 @@ cmd_clients() {
printf ']\n'
}
# Current IP for a selected MAC (resolved fresh each sweep; DHCP may move it).
mac_to_ip() {
ip neigh show dev "$device" 2>/dev/null | awk -v m="$(echo "$1" | tr 'A-F' 'a-f')" '
{ mac=""; for (i=1;i<=NF;i++) if ($i=="lladdr") mac=tolower($(i+1))
@@ -63,6 +65,7 @@ sweep() {
for mac in $clients; do
ip=$(mac_to_ip "$mac")
[ -n "$ip" ] || continue
# ARP reply: "gateway is at <our MAC>", unicast to the client.
if ! /usr/bin/meow arp-reply "$device" "$gateway" "$mac" "$ip"; then
logger -t meow-arp "failed to send unicast reply to $mac ($ip)"
return 1
+38
View File
@@ -2,6 +2,7 @@
# Edit this file, then restart the service: /etc/init.d/meow restart
# Full reference: https://github.com/madeye/meow-rs/blob/main/config.example.yaml
# Combined HTTP + SOCKS5 proxy for LAN clients.
mixed-port: 7890
allow-lan: true
bind-address: "0.0.0.0"
@@ -10,11 +11,27 @@ mode: rule
log-level: info
ipv6: false
# REST API + built-in web panel (served at /ui). The init script overrides
# the address with `--ext-ctl 0.0.0.0:<panel_port>` and, when set, the secret
# with `--secret <secret>` from /etc/config/meow, so LuCI always knows how to
# reach it. OpenWrt's default firewall still blocks WAN-side access.
external-controller: 0.0.0.0:9090
secret: ""
# SO_MARK for DIRECT sockets (loop avoidance on Linux).
routing-mark: 9527
# Transparent-proxy listener for forwarded LAN traffic (gateway / side-router
# mode, enabled in LuCI or via `uci set meow.tproxy.enabled=1`). Must bind a
# non-loopback IPv4 address; the port must match `tproxy_port` in
# /etc/config/meow.
#
# `firewall: false` leaves all rule management to gateway.sh (this package's
# nftables rules) — meow makes no nft calls itself. `udp: true` enables UDP
# TPROXY, which requires `firewall: false`, an IPv4 listen, and the
# deployer-owned fwmark + policy routing that gateway.sh's `tproxy` mode sets
# up. In `redirect` mode gateway.sh uses nat REDIRECT and only TCP is proxied;
# the same listener accepts it (UDP is simply never delivered).
listeners:
- name: tproxy-lan
type: tproxy
@@ -22,8 +39,12 @@ listeners:
port: 7893
udp: true
firewall: false
# A gateway serves many concurrent flows from every LAN client; the 256
# default saturates under a few busy devices and stalls new connections.
# 0 = unlimited.
max-connections: 4096
# Recover hostnames for connections made to raw IPs.
sniffer:
enable: true
override-destination: false
@@ -33,6 +54,8 @@ sniffer:
dns:
enable: true
# dnsmasq owns :53; meow's resolver listens on 1053. LAN DNS is redirected
# here when transparent proxy + DNS hijack are enabled (`dns_port`).
listen: 0.0.0.0:1053
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.0/16
@@ -40,5 +63,20 @@ dns:
- 223.5.5.5
- 8.8.8.8
# Add your proxies and groups here, e.g.:
# proxies:
# - name: "ss-example"
# type: ss
# server: 1.2.3.4
# port: 8388
# cipher: aes-256-gcm
# password: "your-password"
# udp: true
#
# proxy-groups:
# - name: "PROXY"
# type: select
# proxies: ["ss-example", "DIRECT"]
rules:
- MATCH,DIRECT
Regular → Executable
+58 -9
View File
@@ -1,11 +1,32 @@
#!/bin/sh
# Transparent-proxy rules for traffic forwarded through this router.
# Transparent-proxy rules for traffic forwarded through this router
# (gateway or side-router mode). meow's built-in `inet meow_tproxy` table only
# covers the router's own output traffic; this adds `inet meow_gateway`, which
# hooks prerouting on the LAN device. See docs/tproxy-gateway.md.
#
# Modes (`option mode`):
# tproxy REDIRECT for TCP + kernel TPROXY for UDP (default). meow's TCP
# listener recovers the original destination via SO_ORIGINAL_DST,
# which only nat REDIRECT populates, so TCP is never sent through
# kernel TPROXY; UDP has a real IP_TRANSPARENT listener.
# redirect nat REDIRECT, TCP only
#
# Usage: gateway.sh up|down|wait|status
# Settings come from the `tproxy` section of /etc/config/meow.
. /lib/functions.sh
. /lib/functions/network.sh
TABLE="inet meow_gateway"
RULES=/var/run/meow/gateway.nft
# Two marks, deliberately different (see docs/tproxy-gateway.md):
# FWMARK tags the UDP datagrams the mangle chain hands to the TPROXY
# listener; a policy route delivers those marked packets
# locally. TCP never uses it.
# ROUTING_MARK meow's own outbound sockets carry this (SO_MARK, from the
# YAML `routing-mark`), so both capture chains RETURN on it and
# meow's own traffic is never re-proxied into a loop. This MUST
# equal `routing-mark` in the meow YAML config (default 9527).
FWMARK=0x2333
ROUTING_MARK=0x2537
ROUTE_TABLE=233
@@ -24,7 +45,8 @@ load_config() {
}
validate_config() {
case "$mode:$ipv6" in redirect:0|redirect:1|tproxy:0) ;; *) return 1 ;; esac
case "$mode:$ipv6" in redirect:0|redirect:1|tproxy:0) ;; *)
logger -t meow 'gateway: invalid mode/IPv6 combination; IPv6 requires redirect mode'; return 1 ;; esac
for port in "$tproxy_port" "$dns_port"; do
case "$port" in ''|*[!0-9]*) return 1 ;; esac
[ "$port" -ge 1 ] && [ "$port" -le 65535 ] || return 1
@@ -33,9 +55,14 @@ validate_config() {
for cidr in $bypass; do
case "$cidr" in *[!0-9a-fA-F.:/]*|'') return 1 ;; esac
done
local h='[0-9a-f][0-9a-f]'
for mac in $bypass_mac; do
case "$mac" in $h:$h:$h:$h:$h:$h) ;; *) return 1 ;; esac
done
}
port_listening() {
# /proc/net/tcp{,6} list local ports in hex; state 0A is LISTEN.
local hex
hex=$(printf '%04X' "$1")
grep -qE "^ *[0-9]+: [0-9A-F]+:$hex [0-9A-F]+:[0-9A-F]+ 0A" \
@@ -44,6 +71,7 @@ port_listening() {
gen_rules() {
local device="$1" extra4="" extra6="" macs="" cidr mac
for cidr in $bypass; do
case "$cidr" in
*:*) extra6="$extra6, $cidr" ;;
@@ -54,6 +82,12 @@ gen_rules() {
macs="${macs:+$macs, }$mac"
done
# TCP goes through nat REDIRECT in BOTH modes (dstnat chain): meow's TCP
# listener recovers the original destination with SO_ORIGINAL_DST, which
# needs the conntrack DNAT that REDIRECT creates. Kernel TPROXY leaves no
# conntrack entry, so a TPROXY'd TCP flow resolves to the listener's own
# address and meow dials itself — a saturating loop. UDP has no REDIRECT
# equivalent and a real IP_TRANSPARENT listener, so it uses TPROXY.
cat <<-NFT
table inet meow_gateway {
set reserved4 {
@@ -68,9 +102,10 @@ gen_rules() {
type ipv6_addr; flags interval; auto-merge
elements = { ::/128, ::1/128, fc00::/7, fe80::/10, ff00::/8$extra6 }
}
# LAN clients (by MAC) that skip both capture and the DNS hijack.
set bypass_src {
type ether_addr;
NFT
type ether_addr
NFT
[ -n "$macs" ] && echo " elements = { $macs }"
cat <<-NFT
}
@@ -81,7 +116,8 @@ gen_rules() {
meta mark $ROUTING_MARK return
ether saddr @bypass_src return
NFT
[ "$dns_hijack" -eq 1 ] && echo " meta nfproto ipv4 meta l4proto udp th dport 53 redirect to :$dns_port"
[ "$dns_hijack" -eq 1 ] &&
echo " meta nfproto ipv4 meta l4proto udp th dport 53 redirect to :$dns_port"
[ "$ipv6" -eq 1 ] || echo " meta nfproto ipv6 return"
cat <<-NFT
fib daddr type local return
@@ -91,6 +127,7 @@ gen_rules() {
}
NFT
# redirect mode stops at TCP; tproxy mode adds the UDP TPROXY chain.
if [ "$mode" != redirect ]; then
cat <<-NFT
@@ -111,6 +148,7 @@ gen_rules() {
}
NFT
fi
echo "}"
}
@@ -145,18 +183,27 @@ case "$1" in
load_config
validate_config || { logger -t meow "gateway: invalid configuration"; exit 1; }
network_get_device device "$interface"
[ -n "$device" ] || exit 1
[ -n "$device" ] || {
logger -t meow "gateway: no device for interface '$interface'"
exit 1
}
case "$device" in *[!a-zA-Z0-9_.:-]*) exit 1 ;; esac
mkdir -p "${RULES%/*}"
gen_rules "$device" > "$RULES"
nft -c -f "$RULES" || exit 1
if ! nft -c -f "$RULES"; then
logger -t meow 'gateway: invalid rules or missing kernel support; install kmod-nft-socket kmod-nft-tproxy (opkg install, or apk add on newer OpenWrt)'
exit 1
fi
nft delete table $TABLE 2>/dev/null
route_down
nft -f "$RULES" || exit 1
if ! nft -f "$RULES"; then
logger -t meow "gateway: failed to load $RULES"
exit 1
fi
if [ "$mode" = redirect ]; then
logger -t meow "gateway: redirect $device tcp -> :$tproxy_port"
else
route_up || { "$0" down; exit 1; }
route_up || { "$0" down; logger -t meow "gateway: policy routing failed"; exit 1; }
logger -t meow "gateway: tproxy $device tcp(redirect)+udp -> :$tproxy_port"
fi
;;
@@ -165,6 +212,8 @@ case "$1" in
route_down
;;
wait)
# Block until meow binds the tproxy listener (30s cap, then load
# anyway so the rules fail closed rather than leaking traffic).
load_config
i=0
while [ $i -lt 150 ]; do
+5
View File
@@ -1,4 +1,9 @@
#!/bin/sh /etc/rc.common
# procd init for meow's ARP client-steering loop (arp-hijack.sh). Self-gates on
# the `arp_hijack` section of /etc/config/meow: does nothing unless enabled and
# at least the feature is turned on. Separate from the meow service so steering
# can be toggled without restarting the proxy.
USE_PROCD=1
START=96
STOP=9
+35
View File
@@ -1,21 +1,56 @@
config meow 'main'
# Set to '1' to start meow at boot and via /etc/init.d/meow start.
option enabled '0'
# meow YAML configuration (proxies, rules, DNS, listeners, ...).
option config_file '/etc/meow/config.yaml'
# Working directory for GeoIP databases, caches, downloaded rulesets.
option work_dir '/etc/meow'
# Port of the REST API / built-in web panel. Passed to meow as
# `--ext-ctl 0.0.0.0:<port>`, overriding `external-controller` in the YAML.
option panel_port '9090'
# A random API secret is generated on install. Empty binds to loopback only.
# When non-empty it is passed as `--secret`, overriding
# `secret:` in the YAML; LuCI uses it to talk to the API and the panel.
option secret ''
# Transparent proxy for forwarded LAN traffic (gateway / side-router mode).
# Loads an nftables table `inet meow_gateway` that hands traffic arriving on
# the LAN interface to meow's tproxy listener and hijacks DNS to meow's
# resolver. The YAML config must declare a matching non-loopback tproxy
# listener and DNS listener (the shipped default config does).
config transparent 'tproxy'
option enabled '0'
# 'tproxy': REDIRECT for TCP + kernel TPROXY for UDP. 'redirect': TCP only.
option mode 'tproxy'
# Logical OpenWrt interface whose clients are proxied.
option interface 'lan'
# Port of the `type: tproxy` listener in the YAML config.
option tproxy_port '7893'
# Redirect LAN DNS (port 53, any resolver) to meow's DNS listener.
option dns_hijack '1'
# Port of meow's DNS listener (`dns.listen` in the YAML config).
option dns_port '1053'
# Also proxy IPv6. Off by default: fake-ip suppresses AAAA.
option ipv6 '0'
# Extra destination CIDRs that bypass the proxy (besides reserved ranges).
# list bypass '203.0.113.0/24'
# LAN client MACs whose traffic bypasses the proxy entirely (no capture, no
# DNS hijack). Managed from LuCI (Services -> meow -> Clients).
# list bypass_mac 'aa:bb:cc:dd:ee:ff'
# ARP-based client steering (side-router / gateway mode). For SELECTED clients,
# periodically announce this device as the LAN gateway so their traffic is
# transparently proxied without per-client config. This is ARP spoofing: use it
# only for devices you administer on a network you control. OFF by default; only
# the MACs listed here are ever touched. Managed from LuCI (Services -> meow ->
# Clients). Uses meow's built-in unicast ARP sender.
config arp_hijack 'arp'
option enabled '0'
# LAN interface whose clients are steered.
option interface 'lan'
# Gateway IP to impersonate. Empty = this interface's real gateway.
option gateway ''
# Seconds between ARP announcements (caches expire, so we re-announce).
option interval '2'
# Selected client MACs (one per line). Managed from the Clients tab.
# list client 'aa:bb:cc:dd:ee:ff'
Regular → Executable
+16 -7
View File
@@ -1,4 +1,8 @@
#!/bin/sh /etc/rc.common
# procd init script for meow-rs.
# Service settings live in /etc/config/meow (UCI); the proxy configuration
# itself is the regular meow YAML file (default /etc/meow/config.yaml).
USE_PROCD=1
START=95
STOP=10
@@ -18,7 +22,10 @@ start_service() {
config_get_bool tproxy tproxy enabled 0
[ "$enabled" -eq 1 ] || return 0
[ "$config_file" = /etc/meow/config.yaml ] && [ "$work_dir" = /etc/meow ] || return 1
[ "$config_file" = /etc/meow/config.yaml ] && [ "$work_dir" = /etc/meow ] || {
logger -t meow 'managed service requires /etc/meow/config.yaml and work_dir /etc/meow'
return 1
}
if ! "$PROG" --no-external-plugins -d "$work_dir" -f "$config_file" -t >/dev/null 2>&1; then
logger -t meow "config test failed for $config_file, not starting"
@@ -31,6 +38,7 @@ start_service() {
procd_set_param command "$PROG" --no-external-plugins -d "$work_dir" -f "$config_file" \
--ext-ctl "$controller"
[ -n "$secret" ] && procd_append_param command --secret "$secret"
# Restart when either the UCI service settings or the YAML config change.
procd_set_param file /etc/config/meow "$config_file"
procd_set_param respawn 3600 5 5
procd_set_param stdout 1
@@ -39,12 +47,13 @@ start_service() {
procd_close_instance
if [ "$tproxy" -eq 1 ]; then
procd_open_instance gateway
procd_set_param command "$GATEWAY" run
procd_set_param term_timeout 5
procd_set_param stdout 1
procd_set_param stderr 1
procd_close_instance
# A supervised instance owns the wait and rules, including teardown.
procd_open_instance gateway
procd_set_param command "$GATEWAY" run
procd_set_param term_timeout 5
procd_set_param stdout 1
procd_set_param stderr 1
procd_close_instance
else
"$GATEWAY" down
fi
+2
View File
@@ -1,4 +1,6 @@
#!/bin/sh
# Add sections introduced after the first release to preserved configs.
# Preserve existing credentials on upgrades; initialize an empty install.
if [ -z "$(uci -q get meow.main.secret)" ]; then
secret=$(head -c 32 /dev/urandom | hexdump -v -e '1/1 "%02x"')
[ "${#secret}" -eq 64 ] || exit 1
Binary file not shown.