🤞 Sync 2026-09-30 23:44:05

This commit is contained in:
github-actions[bot] committed 2026-09-30 23:44:05 +08:00
1 parent e3fde93105
commit 44bb740ab2
206 files changed
+5736 -2799

No files matched your search

+52
View File
@@ -0,0 +1,52 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=meow
PKG_VERSION?=0.21.2
PKG_RELEASE?=1
include $(INCLUDE_DIR)/package.mk
PKG_MAINTAINER:=Independent meow OpenWrt package <noreply@example.invalid>
define Package/meow
SECTION:=net
CATEGORY:=Network
TITLE:=meow high-performance rule-based proxy core
URL:=https://github.com/meow-rs/meow-rs
LICENSE:=MIT
endef
define Package/meow/description
meow Rust proxy core built from the upstream commit recorded in UPSTREAM.json.
endef
define Build/Prepare
endef
define Build/Compile
endef
define Package/meow/install
@test -s ./prebuilt-meow || { echo "ERROR: meow/prebuilt-meow is not populated yet; run build-core.yml first."; exit 1; }
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_DIR) $(1)/etc/meow
$(INSTALL_DIR) $(1)/etc/uci-defaults
$(INSTALL_DIR) $(1)/usr/share/meow
$(INSTALL_BIN) ./prebuilt-meow $(1)/usr/bin/meow
$(INSTALL_BIN) ./files/meow.init $(1)/etc/init.d/meow
$(INSTALL_DATA) ./files/meow.config $(1)/etc/config/meow
$(INSTALL_DATA) ./files/config.yaml $(1)/etc/meow/config.yaml
$(INSTALL_BIN) ./files/gateway.sh $(1)/usr/share/meow/gateway.sh
$(INSTALL_BIN) ./files/arp-hijack.sh $(1)/usr/share/meow/arp-hijack.sh
$(INSTALL_BIN) ./files/meow-arp.init $(1)/etc/init.d/meow-arp
$(INSTALL_BIN) ./files/meow.uci-defaults $(1)/etc/uci-defaults/80_meow
endef
define Package/meow/conffiles
/etc/config/meow
/etc/meow/config.yaml
endef
$(eval $(call BuildPackage,meow))
+105
View File
@@ -0,0 +1,105 @@
#!/bin/sh
# ARP-based client steering for meow side-router / gateway mode.
#
# For the LAN clients you SELECT, this periodically sends unicast ARP replies
# claiming the network's real gateway IP is at this device's MAC. Those clients
# then send their off-LAN traffic here instead, so meow's transparent proxy
# handles them WITHOUT any per-client configuration and without changing the
# main router's DHCP.
#
# This is ARP spoofing. Use it only to steer devices you own or administer, on
# a network you control. It is OFF by default and touches only the MACs listed
# in the `arp_hijack` section of /etc/config/meow — never the whole LAN.
#
# A client is restored by simply de-selecting it (its ARP cache relearns the
# real gateway within its normal timeout once we stop announcing).
#
# Usage: arp-hijack.sh clients | run | once
# Settings come from the `arp_hijack` section of /etc/config/meow.
. /lib/functions.sh
. /lib/functions/network.sh
clients=""
add_client() { clients="$clients $1"; }
load_config() {
config_load meow
config_get_bool enabled arp enabled 0
config_get interface arp interface lan
config_get gateway arp gateway ''
config_get interval arp interval 2
config_list_foreach arp client add_client
network_get_device device "$interface"
[ -n "$gateway" ] || network_get_gateway gateway "$interface"
}
cmd_clients() {
load_config
local sel
sel=" $(echo "$clients" | tr 'A-F' 'a-f') "
printf '['
[ -n "$device" ] && ip neigh show dev "$device" 2>/dev/null | awk -v sel="$sel" '
{
ip=$1; mac=""; st=$NF
for (i=1;i<=NF;i++) if ($i=="lladdr") mac=$(i+1)
if (mac=="" || index(ip,":")) next
lm=tolower(mac)
s=(index(sel," " lm " ")?"true":"false")
printf "%s{\"ip\":\"%s\",\"mac\":\"%s\",\"state\":\"%s\",\"selected\":%s}", (c++?",":""), ip, mac, st, s
}'
printf ']\n'
}
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))
if (mac==m && !index($1,":")) { print $1; exit } }'
}
sweep() {
local mac ip
for mac in $clients; do
ip=$(mac_to_ip "$mac")
[ -n "$ip" ] || continue
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
fi
done
}
precheck() {
case "$interval" in ''|*[!0-9]*) logger -t meow-arp 'interval must be an integer from 1 to 3600'; return 1 ;; esac
[ "$interval" -ge 1 ] && [ "$interval" -le 3600 ] || return 1
[ -n "$device" ] || { logger -t meow-arp "no device for interface '$interface'"; return 1; }
[ -n "$gateway" ] || { logger -t meow-arp "no gateway for '$interface'; set arp.gateway"; return 1; }
return 0
}
case "$1" in
clients)
cmd_clients
;;
once)
load_config
precheck || exit 1
sweep
;;
run)
load_config
[ "$enabled" -eq 1 ] || { logger -t meow-arp "disabled; not steering"; exit 0; }
precheck || exit 1
[ -n "$clients" ] || { logger -t meow-arp "no clients selected; idle"; }
logger -t meow-arp "steering [$clients ] via $device as gateway $gateway"
while :; do
sweep || exit 1
sleep "$interval"
done
;;
*)
echo "usage: $0 clients|run|once" >&2
exit 1
;;
esac
+44
View File
@@ -0,0 +1,44 @@
# Default meow-rs configuration shipped with the OpenWrt package.
# 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
mixed-port: 7890
allow-lan: true
bind-address: "0.0.0.0"
mode: rule
log-level: info
ipv6: false
external-controller: 0.0.0.0:9090
secret: ""
routing-mark: 9527
listeners:
- name: tproxy-lan
type: tproxy
listen: 0.0.0.0
port: 7893
udp: true
firewall: false
max-connections: 4096
sniffer:
enable: true
override-destination: false
sniff:
TLS: { ports: [443] }
HTTP: { ports: [80] }
dns:
enable: true
listen: 0.0.0.0:1053
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.0/16
nameserver:
- 223.5.5.5
- 8.8.8.8
rules:
- MATCH,DIRECT
+185
View File
@@ -0,0 +1,185 @@
#!/bin/sh
# Transparent-proxy rules for traffic forwarded through this router.
. /lib/functions.sh
. /lib/functions/network.sh
TABLE="inet meow_gateway"
RULES=/var/run/meow/gateway.nft
FWMARK=0x2333
ROUTING_MARK=0x2537
ROUTE_TABLE=233
load_config() {
config_load meow
config_get interface tproxy interface lan
config_get mode tproxy mode tproxy
config_get tproxy_port tproxy tproxy_port 7893
config_get_bool dns_hijack tproxy dns_hijack 1
config_get dns_port tproxy dns_port 1053
config_get_bool ipv6 tproxy ipv6 0
config_get bypass tproxy bypass ''
config_get bypass_mac tproxy bypass_mac ''
bypass_mac=$(echo "$bypass_mac" | tr 'A-F' 'a-f')
}
validate_config() {
case "$mode:$ipv6" in redirect:0|redirect:1|tproxy:0) ;; *) 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
done
case "$interface" in ''|*[!a-zA-Z0-9_.:-]*) return 1 ;; esac
for cidr in $bypass; do
case "$cidr" in *[!0-9a-fA-F.:/]*|'') return 1 ;; esac
done
}
port_listening() {
local hex
hex=$(printf '%04X' "$1")
grep -qE "^ *[0-9]+: [0-9A-F]+:$hex [0-9A-F]+:[0-9A-F]+ 0A" \
/proc/net/tcp /proc/net/tcp6 2>/dev/null
}
gen_rules() {
local device="$1" extra4="" extra6="" macs="" cidr mac
for cidr in $bypass; do
case "$cidr" in
*:*) extra6="$extra6, $cidr" ;;
*) extra4="$extra4, $cidr" ;;
esac
done
for mac in $bypass_mac; do
macs="${macs:+$macs, }$mac"
done
cat <<-NFT
table inet meow_gateway {
set reserved4 {
type ipv4_addr; flags interval; auto-merge
elements = {
0.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 127.0.0.0/8,
169.254.0.0/16, 172.16.0.0/12, 192.168.0.0/16,
224.0.0.0/4, 240.0.0.0/4$extra4
}
}
set reserved6 {
type ipv6_addr; flags interval; auto-merge
elements = { ::/128, ::1/128, fc00::/7, fe80::/10, ff00::/8$extra6 }
}
set bypass_src {
type ether_addr;
NFT
[ -n "$macs" ] && echo " elements = { $macs }"
cat <<-NFT
}
chain dstnat {
type nat hook prerouting priority dstnat - 5; policy accept;
iifname != "$device" return
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"
[ "$ipv6" -eq 1 ] || echo " meta nfproto ipv6 return"
cat <<-NFT
fib daddr type local return
ip daddr @reserved4 return
ip6 daddr @reserved6 return
meta l4proto tcp redirect to :$tproxy_port
}
NFT
if [ "$mode" != redirect ]; then
cat <<-NFT
chain mangle_tproxy {
type filter hook prerouting priority mangle; policy accept;
iifname != "$device" return
meta mark $ROUTING_MARK return
ether saddr @bypass_src return
NFT
[ "$dns_hijack" -eq 1 ] && echo " meta l4proto udp th dport 53 return"
[ "$ipv6" -eq 1 ] || echo " meta nfproto ipv6 return"
cat <<-NFT
fib daddr type local return
ip daddr @reserved4 return
ip6 daddr @reserved6 return
meta l4proto udp socket transparent 1 meta mark set $FWMARK accept
meta l4proto udp tproxy to :$tproxy_port meta mark set $FWMARK accept
}
NFT
fi
echo "}"
}
route_up() {
ip rule add fwmark $FWMARK lookup $ROUTE_TABLE || return 1
ip route replace local 0.0.0.0/0 dev lo table $ROUTE_TABLE || return 1
if [ "$ipv6" -eq 1 ]; then
ip -6 rule add fwmark $FWMARK lookup $ROUTE_TABLE
ip -6 route replace local ::/0 dev lo table $ROUTE_TABLE
fi
}
route_down() {
while ip rule del fwmark $FWMARK lookup $ROUTE_TABLE 2>/dev/null; do :; done
ip route flush table $ROUTE_TABLE 2>/dev/null
while ip -6 rule del fwmark $FWMARK lookup $ROUTE_TABLE 2>/dev/null; do :; done
ip -6 route flush table $ROUTE_TABLE 2>/dev/null
return 0
}
case "$1" in
run)
child=''
trap '[ -z "$child" ] || kill "$child" 2>/dev/null; "$0" down; exit 0' TERM INT
"$0" wait & child=$!
wait "$child" || exit 1
child=''
"$0" up || exit 1
while :; do sleep 3600 & child=$!; wait "$child"; done
;;
up)
load_config
validate_config || { logger -t meow "gateway: invalid configuration"; exit 1; }
network_get_device device "$interface"
[ -n "$device" ] || 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
nft delete table $TABLE 2>/dev/null
route_down
nft -f "$RULES" || exit 1
if [ "$mode" = redirect ]; then
logger -t meow "gateway: redirect $device tcp -> :$tproxy_port"
else
route_up || { "$0" down; exit 1; }
logger -t meow "gateway: tproxy $device tcp(redirect)+udp -> :$tproxy_port"
fi
;;
down)
nft delete table $TABLE 2>/dev/null
route_down
;;
wait)
load_config
i=0
while [ $i -lt 150 ]; do
port_listening "$tproxy_port" && exit 0
sleep 0.2
i=$((i + 1))
done
logger -t meow "gateway: :$tproxy_port not listening after 30s"
exit 0
;;
status)
nft list table $TABLE
;;
*)
echo "usage: $0 up|down|wait|status" >&2
exit 1
;;
esac
+24
View File
@@ -0,0 +1,24 @@
#!/bin/sh /etc/rc.common
USE_PROCD=1
START=96
STOP=9
PROG=/usr/share/meow/arp-hijack.sh
start_service() {
local enabled
config_load meow
config_get_bool enabled arp enabled 0
[ "$enabled" -eq 1 ] || return 0
procd_open_instance meow-arp
procd_set_param command "$PROG" run
procd_set_param respawn 3600 5 5
procd_set_param stdout 1
procd_set_param stderr 1
procd_close_instance
}
service_triggers() {
procd_add_reload_trigger "meow"
}
+21
View File
@@ -0,0 +1,21 @@
config meow 'main'
option enabled '0'
option config_file '/etc/meow/config.yaml'
option work_dir '/etc/meow'
option panel_port '9090'
option secret ''
config transparent 'tproxy'
option enabled '0'
option mode 'tproxy'
option interface 'lan'
option tproxy_port '7893'
option dns_hijack '1'
option dns_port '1053'
option ipv6 '0'
config arp_hijack 'arp'
option enabled '0'
option interface 'lan'
option gateway ''
option interval '2'
+64
View File
@@ -0,0 +1,64 @@
#!/bin/sh /etc/rc.common
USE_PROCD=1
START=95
STOP=10
PROG=/usr/bin/meow
GATEWAY=/usr/share/meow/gateway.sh
start_service() {
local enabled config_file work_dir panel_port secret tproxy controller
config_load meow
config_get_bool enabled main enabled 0
config_get config_file main config_file /etc/meow/config.yaml
config_get work_dir main work_dir /etc/meow
config_get panel_port main panel_port 9090
config_get secret main secret ''
config_get_bool tproxy tproxy enabled 0
[ "$enabled" -eq 1 ] || return 0
[ "$config_file" = /etc/meow/config.yaml ] && [ "$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"
return 1
fi
controller="127.0.0.1:$panel_port"
[ -z "$secret" ] || controller="0.0.0.0:$panel_port"
procd_open_instance meow
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"
procd_set_param file /etc/config/meow "$config_file"
procd_set_param respawn 3600 5 5
procd_set_param stdout 1
procd_set_param stderr 1
procd_set_param limits nofile="1000000 1000000"
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
else
"$GATEWAY" down
fi
}
stop_service() {
"$GATEWAY" down
}
service_triggers() {
procd_add_reload_trigger "meow"
}
reload_service() {
stop
start
}
+28
View File
@@ -0,0 +1,28 @@
#!/bin/sh
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
uci set "meow.main.secret=$secret"
fi
if ! uci -q get meow.tproxy >/dev/null; then
uci -q batch <<-UCI
set meow.tproxy=transparent
set meow.tproxy.enabled='0'
set meow.tproxy.mode='tproxy'
set meow.tproxy.interface='lan'
set meow.tproxy.tproxy_port='7893'
set meow.tproxy.dns_hijack='1'
set meow.tproxy.dns_port='1053'
set meow.tproxy.ipv6='0'
UCI
fi
if ! uci -q get meow.arp >/dev/null; then
uci -q batch <<-UCI
set meow.arp=arp_hijack
set meow.arp.enabled='0'
set meow.arp.interface='lan'
set meow.arp.interval='2'
UCI
fi
uci -q commit meow
exit 0
BIN
View File
Binary file not shown.