update 2026-05-10 09:48:30

This commit is contained in:
action committed 2026-05-10 09:48:30 +08:00
1 parent 95ac96413e
commit 0b362674f1
6244 files changed
+2992248

No files matched your search

File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+129
View File
@@ -0,0 +1,129 @@
config config 'config'
option 'init' '1'
option 'enabled' '0'
option 'profile' 'file:example.json'
option 'start_delay' '0'
option 'scheduled_restart' '0'
option 'scheduled_restart_cron' '0 3 * * *'
option 'test_profile' '1'
option 'core_only' '0'
config procd 'procd'
option 'fast_reload' '0'
config core 'core'
option 'redirect_inbound_tag' 'redirect-in'
option 'tproxy_inbound_tag' 'tproxy-in'
option 'tun_inbound_tag' 'tun-in'
option 'dns_inbound_tag' 'dns-in'
option 'fake_ip_dns_server_tag' 'fake-ip-dns-server'
config mixin 'mixin'
option 'log_disabled' '0'
option 'log_level' 'info'
option 'log_timestamp' '1'
option 'dns_independent_cache' '1'
option 'dns_reverse_mapping' '1'
option 'cache_enabled' '1'
option 'cache_store_fakeip' '1'
option 'cache_store_rdrc' '1'
option 'external_control_ui_path' 'ui'
option 'external_control_ui_download_url' 'https://github.com/Zephyruso/zashboard/releases/latest/download/dist-cdn-fonts.zip'
config proxy 'proxy'
option 'enabled' '1'
option 'ipv4_dns_hijack' '1'
option 'ipv6_dns_hijack' '1'
option 'ipv4_proxy' '1'
option 'ipv6_proxy' '1'
option 'fake_ip_ping_hijack' '1'
option 'tcp_mode' 'redirect'
option 'udp_mode' 'tun'
option 'router_proxy' '1'
option 'lan_proxy' '1'
list 'lan_inbound_interface' 'lan'
list 'reserved_ip' '0.0.0.0/8'
list 'reserved_ip' '10.0.0.0/8'
list 'reserved_ip' '127.0.0.0/8'
list 'reserved_ip' '100.64.0.0/10'
list 'reserved_ip' '169.254.0.0/16'
list 'reserved_ip' '172.16.0.0/12'
list 'reserved_ip' '192.168.0.0/16'
list 'reserved_ip' '224.0.0.0/4'
list 'reserved_ip' '240.0.0.0/4'
list 'reserved_ip6' '::/128'
list 'reserved_ip6' '::1/128'
list 'reserved_ip6' '::ffff:0:0/96'
list 'reserved_ip6' '100::/64'
list 'reserved_ip6' '64:ff9b::/96'
list 'reserved_ip6' '2001::/32'
list 'reserved_ip6' '2001:10::/28'
list 'reserved_ip6' '2001:20::/28'
list 'reserved_ip6' '2001:db8::/32'
list 'reserved_ip6' '2002::/16'
list 'reserved_ip6' 'fc00::/7'
list 'reserved_ip6' 'fe80::/10'
list 'reserved_ip6' 'ff00::/8'
option 'bypass_china_mainland_ip' '0'
option 'bypass_china_mainland_ip6' '0'
option 'proxy_tcp_dport' '0-65535'
option 'proxy_udp_dport' '0-65535'
list 'bypass_dscp' '4'
option 'tun_timeout' '30'
option 'tun_interval' '1'
config router_access_control
option 'enabled' '1'
list 'user' 'dnsmasq'
list 'user' 'ftp'
list 'user' 'logd'
list 'user' 'nobody'
list 'user' 'ntp'
list 'user' 'ubus'
list 'group' 'dnsmasq'
list 'group' 'ftp'
list 'group' 'logd'
list 'group' 'nogroup'
list 'group' 'ntp'
list 'group' 'ubus'
list 'cgroup' 'services/adguardhome'
list 'cgroup' 'services/aria2'
list 'cgroup' 'services/dnsmasq'
list 'cgroup' 'services/netbird'
list 'cgroup' 'services/qbittorrent'
list 'cgroup' 'services/sysntpd'
list 'cgroup' 'services/tailscale'
list 'cgroup' 'services/zerotier'
option 'dns' '0'
option 'proxy' '0'
config router_access_control
option 'enabled' '1'
option 'dns' '1'
option 'proxy' '1'
config lan_access_control
option 'enabled' '1'
option 'dns' '1'
option 'proxy' '1'
config routing 'routing'
option 'tproxy_fw_mark' '0x80'
option 'tproxy_fw_mask' '0xFF'
option 'tun_fw_mark' '0x81'
option 'tun_fw_mask' '0xFF'
option 'tproxy_rule_pref' '1024'
option 'tun_rule_pref' '1025'
option 'tproxy_route_table' '80'
option 'tun_route_table' '81'
option 'cgroup_id' '0x07250725'
option 'cgroup_name' 'momo'
option 'dummy_device' 'momo-dummy'
config log 'log'
option 'scheduled_clear' '1'
option 'scheduled_clear_cron' '*/5 * * * *'
option 'scheduled_clear_size_limit' '1'
option 'scheduled_clear_size_limit_unit' 'MB'
config placeholder 'placeholder'
+563
View File
@@ -0,0 +1,563 @@
#!/bin/sh /etc/rc.common
START=99
STOP=10
USE_PROCD=1
PROG="/usr/bin/sing-box"
. "$IPKG_INSTROOT/etc/momo/scripts/include.sh"
extra_command 'update_subscription' 'Update subscription by section id'
extra_command 'clear_logs' 'Clear logs if size exceeds the limit'
boot_func() {
# prepare files
prepare_files
# load config
config_load momo
# start delay
local enabled start_delay
config_get_bool enabled "config" "enabled" 0
config_get start_delay "config" "start_delay" 0
if [ "$enabled" = 1 ] && [ "$start_delay" -gt 0 ]; then
log "App" "Start after $start_delay seconds."
sleep "$start_delay"
fi
# start
start
}
boot() {
boot_func < /dev/null > /dev/null 2>&1 &
}
start_service() {
# prepare files
prepare_files
# load config
config_load momo
# check if enabled
local enabled
config_get_bool enabled "config" "enabled" 0
if [ "$enabled" = 0 ]; then
log "App" "Disabled."
log "App" "Exit."
return
fi
# start
log "App" "Enabled."
log "App" "Start."
# check if executable
if [ ! -x $PROG ]; then
log "App" "Core is not executable."
log "App" "Exit."
return
fi
# get config
## app config
local scheduled_restart scheduled_restart_cron profile test_profile core_only
config_get_bool scheduled_restart "config" "scheduled_restart" 0
config_get scheduled_restart_cron "config" "scheduled_restart_cron"
config_get profile "config" "profile"
config_get_bool test_profile "config" "test_profile" 0
config_get_bool core_only "config" "core_only" 0
## procd config
### general
local fast_reload
config_get_bool fast_reload "procd" "fast_reload" 0
### rlimit
local rlimit_address_space_soft rlimit_address_space_hard rlimit_data_soft rlimit_data_hard rlimit_stack_soft rlimit_stack_hard rlimit_nofile_soft rlimit_nofile_hard
config_get rlimit_address_space_soft "procd" "rlimit_address_space_soft"
config_get rlimit_address_space_hard "procd" "rlimit_address_space_hard"
config_get rlimit_data_soft "procd" "rlimit_data_soft"
config_get rlimit_data_hard "procd" "rlimit_data_hard"
config_get rlimit_stack_soft "procd" "rlimit_stack_soft"
config_get rlimit_stack_hard "procd" "rlimit_stack_hard"
config_get rlimit_nofile_soft "procd" "rlimit_nofile_soft" "$(ulimit -Sn)"
config_get rlimit_nofile_hard "procd" "rlimit_nofile_hard" "$(ulimit -Hn)"
## core config
local redirect_inbound_tag tproxy_inbound_tag tun_inbound_tag dns_inbound_tag fake_ip_dns_server_tag
config_get redirect_inbound_tag "core" "redirect_inbound_tag"
config_get tproxy_inbound_tag "core" "tproxy_inbound_tag"
config_get tun_inbound_tag "core" "tun_inbound_tag"
config_get dns_inbound_tag "core" "dns_inbound_tag"
config_get fake_ip_dns_server_tag "core" "fake_ip_dns_server_tag"
## proxy config
local proxy_enabled ipv4_dns_hijack ipv6_dns_hijack tcp_mode udp_mode
config_get_bool proxy_enabled "proxy" "enabled" 0
config_get_bool ipv4_dns_hijack "proxy" "ipv4_dns_hijack" 0
config_get_bool ipv6_dns_hijack "proxy" "ipv6_dns_hijack" 0
config_get tcp_mode "proxy" "tcp_mode"
config_get udp_mode "proxy" "udp_mode"
## log config
local log_scheduled_clear log_scheduled_clear_cron
config_get_bool log_scheduled_clear "log" "scheduled_clear" 0
config_get log_scheduled_clear_cron "log" "scheduled_clear_cron"
# get profile
local profile_type; profile_type=$(echo "$profile" | cut -d ':' -f 1)
local profile_id; profile_id=$(echo "$profile" | cut -d ':' -f 2)
if [ "$profile_type" = "file" ]; then
local profile_name; profile_name="$profile_id"
local profile_file; profile_file="$PROFILES_DIR/$profile_name"
log "Profile" "Use file: $profile_name."
if [ ! -f "$profile_file" ]; then
log "Profile" "File not found."
log "App" "Exit."
return
fi
cp -f "$profile_file" "$RUN_PROFILE_PATH"
elif [ "$profile_type" = "subscription" ]; then
local subscription_section; subscription_section="$profile_id"
local subscription_name subscription_prefer
config_get subscription_name "$subscription_section" "name"
config_get subscription_prefer "$subscription_section" "prefer" "remote"
log "Profile" "Use subscription: $subscription_name."
local subscription_file; subscription_file="$SUBSCRIPTIONS_DIR/$subscription_section.json"
if [ "$subscription_prefer" = "remote" ] || [ ! -f "$subscription_file" ]; then
update_subscription "$subscription_section"
fi
if [ ! -f "$subscription_file" ]; then
log "Profile" "Subscription file not found."
log "App" "Exit."
return
fi
cp -f "$subscription_file" "$RUN_PROFILE_PATH"
else
log "Profile" "No profile/subscription selected."
log "App" "Exit."
return
fi
# mixin
if [ "$core_only" = 0 ]; then
log "Mixin" "Mixin config."
ucode -S "$MIXIN_UC"
fi
# check profile
if [ "$core_only" = 0 ] && [ "$proxy_enabled" = 1 ]; then
log "Profile" "Checking..."
if [ "$ipv4_dns_hijack" = 1 ] || [ "$ipv6_dns_hijack" = 1 ]; then
local dns_port; dns_port=$(jsonfilter -q -i "$RUN_PROFILE_PATH" -e "@['inbounds'][*]" | jsonfilter -q -a -e "@[@['tag']='$dns_inbound_tag']" | jsonfilter -q -a -e "@[@['type']='direct']" | jsonfilter -q -e "@['listen_port']")
if [ -z "$dns_port" ]; then
log "Profile" "Check failed."
log "Profile" "DNS inbound not found, should be tagged with $dns_inbound_tag and define listen_port."
log "App" "Exit."
return
fi
fi
if [ "$tcp_mode" = "redirect" ]; then
local redirect_port; redirect_port=$(jsonfilter -q -i "$RUN_PROFILE_PATH" -e "@['inbounds'][*]" | jsonfilter -q -a -e "@[@['tag']='$redirect_inbound_tag']" | jsonfilter -q -a -e "@[@['type']='redirect']" | jsonfilter -q -e "@['listen_port']")
if [ -z "$redirect_port" ]; then
log "Profile" "Check failed."
log "Profile" "Redirect inbound not found, should be tagged with $redirect_inbound_tag and define listen_port."
log "App" "Exit."
return
fi
fi
if [ "$tcp_mode" = "tproxy" ] || [ "$udp_mode" = "tproxy" ]; then
local tproxy_port; tproxy_port=$(jsonfilter -q -i "$RUN_PROFILE_PATH" -e "@['inbounds'][*]" | jsonfilter -q -a -e "@[@['tag']='$tproxy_inbound_tag']" | jsonfilter -q -a -e "@[@['type']='tproxy']" | jsonfilter -q -e "@['listen_port']")
if [ -z "$tproxy_port" ]; then
log "Profile" "Check failed."
log "Profile" "TPROXY inbound not found, should be tagged with $tproxy_inbound_tag and define listen_port."
log "App" "Exit."
return
fi
fi
if [ "$tcp_mode" = "tun" ] || [ "$udp_mode" = "tun" ]; then
local tun_device; tun_device=$(jsonfilter -q -i "$RUN_PROFILE_PATH" -e "@['inbounds'][*]" | jsonfilter -q -a -e "@[@['tag']='$tun_inbound_tag']" | jsonfilter -q -a -e "@[@['type']='tun']" | jsonfilter -q -e "@['interface_name']")
if [ -z "$tun_device" ]; then
log "Profile" "Check failed."
log "Profile" "TUN inbound not found, should be tagged with $tun_inbound_tag and define interface_name."
log "App" "Exit."
return
fi
fi
log "Profile" "Check passed."
fi
# test profile
if [ "$test_profile" = 1 ]; then
log "Profile" "Testing..."
if $PROG -D "$RUN_DIR" check >> "$CORE_LOG_PATH" 2>&1; then
log "Profile" "Test passed."
else
log "Profile" "Test failed."
log "Profile" "Please check the core log to find out the problem."
log "App" "Exit."
return
fi
fi
# start core
log "Core" "Start."
procd_open_instance momo
procd_set_param command /bin/sh -c "$PROG -D $RUN_DIR run --disable-color >> $CORE_LOG_PATH 2>&1"
procd_set_param file "$RUN_PROFILE_PATH"
if [ "$fast_reload" = 1 ]; then
procd_set_param reload_signal HUP
fi
procd_set_param pidfile "$PID_FILE_PATH"
procd_set_param respawn
procd_append_param limits as="${rlimit_address_space_soft:-unlimited} ${rlimit_address_space_hard:-unlimited}"
procd_append_param limits data="${rlimit_data_soft:-unlimited} ${rlimit_data_hard:-unlimited}"
procd_append_param limits stack="${rlimit_stack_soft:-unlimited} ${rlimit_stack_hard:-unlimited}"
procd_append_param limits nofile="${rlimit_nofile_soft} ${rlimit_nofile_hard}"
procd_close_instance
# cron
local reload_cron; reload_cron=0
if [ "$scheduled_restart" = 1 ] && [ -n "$scheduled_restart_cron" ]; then
log "App" "Set scheduled restart."
echo "$scheduled_restart_cron /etc/init.d/momo restart #momo scheduled restart" >> "/etc/crontabs/root"
reload_cron=1
fi
if [ "$log_scheduled_clear" = 1 ] && [ -n "$log_scheduled_clear_cron" ]; then
log "App" "Set log scheduled clear."
echo "$log_scheduled_clear_cron /etc/init.d/momo clear_logs #momo log scheduled clear" >> "/etc/crontabs/root"
reload_cron=1
fi
if [ "$reload_cron" = 1 ]; then
/etc/init.d/cron restart
fi
# set started flag
touch "$STARTED_FLAG_PATH"
}
service_started() {
# check if started
if [ ! -f "$STARTED_FLAG_PATH" ]; then
return
fi
# load config
config_load momo
# check if proxy enabled
local proxy_enabled
config_get_bool proxy_enabled "proxy" "enabled" 0
if [ "$proxy_enabled" = 0 ]; then
log "Proxy" "Disabled."
return
fi
# get config
## app config
local core_only
config_get_bool core_only "config" "core_only" 0
## core
local tun_inbound_tag fake_ip_dns_server_tag
config_get tun_inbound_tag "core" "tun_inbound_tag"
config_get fake_ip_dns_server_tag "core" "fake_ip_dns_server_tag"
## proxy config
### general
local ipv4_proxy ipv6_proxy tcp_mode udp_mode tun_timeout tun_interval
config_get_bool ipv4_proxy "proxy" "ipv4_proxy" 0
config_get_bool ipv6_proxy "proxy" "ipv6_proxy" 0
config_get tcp_mode "proxy" "tcp_mode"
config_get udp_mode "proxy" "udp_mode"
config_get tun_timeout "proxy" "tun_timeout" 30
config_get tun_interval "proxy" "tun_interval" 1
## routing config
local tproxy_fw_mark tproxy_fw_mask tun_fw_mark tun_fw_mask tproxy_rule_pref tun_rule_pref tproxy_route_table tun_route_table cgroup_id cgroup_name dummy_device
config_get tproxy_fw_mark "routing" "tproxy_fw_mark" "0x80"
config_get tproxy_fw_mask "routing" "tproxy_fw_mask" "0xFF"
config_get tun_fw_mark "routing" "tun_fw_mark" "0x81"
config_get tun_fw_mask "routing" "tun_fw_mask" "0xFF"
config_get tproxy_rule_pref "routing" "tproxy_rule_pref" "1024"
config_get tun_rule_pref "routing" "tun_rule_pref" "1025"
config_get tproxy_route_table "routing" "tproxy_route_table" "80"
config_get tun_route_table "routing" "tun_route_table" "81"
config_get cgroup_id "routing" "cgroup_id" "0x12061206"
config_get cgroup_name "routing" "cgroup_name" "momo"
config_get dummy_device "routing" "dummy_device" "momo-dummy"
# prepare config
local tproxy_enable; tproxy_enable=0
if [ "$tcp_mode" = "tproxy" ] || [ "$udp_mode" = "tproxy" ]; then
tproxy_enable=1
fi
local tun_enable; tun_enable=0
if [ "$tcp_mode" = "tun" ] || [ "$udp_mode" = "tun" ]; then
tun_enable=1
fi
local tun_device; tun_device=$(jsonfilter -q -i "$RUN_PROFILE_PATH" -e "@['inbounds'][*]" | jsonfilter -q -a -e "@[@['tag']='$tun_inbound_tag']" | jsonfilter -q -a -e "@[@['type']='tun']" | jsonfilter -q -e "@['interface_name']")
local fake_ip6_range; fake_ip6_range=$(jsonfilter -q -i "$RUN_PROFILE_PATH" -e "@['dns']['servers'][*]" | jsonfilter -q -a -e "@[@['tag']='$fake_ip_dns_server_tag']" | jsonfilter -q -e "@['inet6_range']")
if [ "$core_only" = 0 ]; then
# proxy
log "Proxy" "Enabled."
# wait for tun device online
if [ "$tun_enable" = 1 ]; then
log "Proxy" "Waiting for tun device online within $tun_timeout seconds..."
while [ "$tun_timeout" -gt 0 ]; do
if ip -j link show dev "$tun_device" 2>/dev/null | jsonfilter -q -e "@[@['flags'][@='UP']]" > /dev/null 2>&1; then
log "Proxy" "TUN device is online."
break
fi
tun_timeout=$((tun_timeout - tun_interval))
sleep "$tun_interval"
done
if [ "$tun_timeout" -le 0 ]; then
log "Proxy" "Timeout, TUN device is not online."
log "App" "Exit."
return
fi
fi
# fix compatible with dockerd
## cgroupfs-mount
### when cgroupfs-mount is installed, cgroupv1 will mounted instead of cgroupv2, we need to create cgroup manually
if mount | grep -q -w "^cgroup"; then
mkdir -p "/sys/fs/cgroup/net_cls/$cgroup_name"
echo "$cgroup_id" > "/sys/fs/cgroup/net_cls/$cgroup_name/net_cls.classid"
cat "$PID_FILE_PATH" > "/sys/fs/cgroup/net_cls/$cgroup_name/cgroup.procs"
fi
## kmod-br-netfilter
### when kmod-br-netfilter is loaded, bridge-nf-call-iptables and bridge-nf-call-ip6tables are set to 1, we need to set them to 0 if tproxy is enabled
if [ "$tproxy_enable" = 1 ] && (lsmod | grep -q br_netfilter); then
if [ "$ipv4_proxy" = 1 ]; then
local bridge_nf_call_iptables; bridge_nf_call_iptables=$(sysctl -e -n net.bridge.bridge-nf-call-iptables)
if [ "$bridge_nf_call_iptables" = 1 ]; then
touch "$BRIDGE_NF_CALL_IPTABLES_FLAG_PATH"
sysctl -q -w net.bridge.bridge-nf-call-iptables=0
fi
fi
if [ "$ipv6_proxy" = 1 ]; then
local bridge_nf_call_ip6tables; bridge_nf_call_ip6tables=$(sysctl -e -n net.bridge.bridge-nf-call-ip6tables)
if [ "$bridge_nf_call_ip6tables" = 1 ]; then
touch "$BRIDGE_NF_CALL_IP6TABLES_FLAG_PATH"
sysctl -q -w net.bridge.bridge-nf-call-ip6tables=0
fi
fi
fi
# ip route and rule
if [ "$tproxy_enable" = 1 ]; then
if [ "$ipv4_proxy" = 1 ]; then
ip -4 route add local default dev lo table "$tproxy_route_table"
ip -4 rule add pref "$tproxy_rule_pref" fwmark "$tproxy_fw_mark/$tproxy_fw_mask" table "$tproxy_route_table"
fi
if [ "$ipv6_proxy" = 1 ]; then
ip -6 route add local default dev lo table "$tproxy_route_table"
ip -6 rule add pref "$tproxy_rule_pref" fwmark "$tproxy_fw_mark/$tproxy_fw_mask" table "$tproxy_route_table"
fi
fi
if [ "$tun_enable" = 1 ]; then
if [ "$ipv4_proxy" = 1 ]; then
ip -4 route add unicast default dev "$tun_device" table "$tun_route_table"
ip -4 rule add pref "$tun_rule_pref" fwmark "$tun_fw_mark/$tun_fw_mask" table "$tun_route_table"
fi
if [ "$ipv6_proxy" = 1 ]; then
ip -6 route add unicast default dev "$tun_device" table "$tun_route_table"
ip -6 rule add pref "$tun_rule_pref" fwmark "$tun_fw_mark/$tun_fw_mask" table "$tun_route_table"
fi
$FIREWALL_INCLUDE_SH
fi
if [ -n "$fake_ip6_range" ]; then
ip link add "$dummy_device" type dummy
ip link set dev "$dummy_device" up
ip -6 route add "$fake_ip6_range" dev "$dummy_device"
fi
# hijack
utpl -S "$HIJACK_UT" | nft -f -
# check hijack
if nft list tables | grep -q momo; then
log "Proxy" "Hijack successful."
else
log "Proxy" "Hijack failed."
log "App" "Exit."
fi
fi
}
service_stopped() {
cleanup
}
reload_service() {
cleanup
start
}
service_triggers() {
procd_add_reload_trigger "momo"
}
cleanup() {
# load config
config_load momo
# get config
## routing config
local tproxy_route_table tun_route_table dummy_device
config_get tproxy_route_table "routing" "tproxy_route_table" "80"
config_get tun_route_table "routing" "tun_route_table" "81"
config_get dummy_device "routing" "dummy_device" "momo-dummy"
# delete routing policy
ip -4 rule del table "$tproxy_route_table" > /dev/null 2>&1
ip -4 rule del table "$tun_route_table" > /dev/null 2>&1
ip -6 rule del table "$tproxy_route_table" > /dev/null 2>&1
ip -6 rule del table "$tun_route_table" > /dev/null 2>&1
# delete routing table
ip -4 route flush table "$tproxy_route_table" > /dev/null 2>&1
ip -4 route flush table "$tun_route_table" > /dev/null 2>&1
ip -6 route flush table "$tproxy_route_table" > /dev/null 2>&1
ip -6 route flush table "$tun_route_table" > /dev/null 2>&1
# delete dummy device
ip -6 link del "$dummy_device" > /dev/null 2>&1
# delete hijack
nft delete table inet momo > /dev/null 2>&1
local handles handle
handles=$(nft --json list table inet fw4 | jsonfilter -q -e "@['nftables'][*]['rule']" | jsonfilter -q -a -e "@[@['chain']='input']" | jsonfilter -q -a -e "@[@['comment']='momo']" | jsonfilter -q -a -e "@[*]['handle']")
for handle in $handles; do
nft delete rule inet fw4 input handle "$handle"
done
handles=$(nft --json list table inet fw4 | jsonfilter -q -e "@['nftables'][*]['rule']" | jsonfilter -q -a -e "@[@['chain']='forward']" | jsonfilter -q -a -e "@[@['comment']='momo']" | jsonfilter -q -a -e "@[*]['handle']")
for handle in $handles; do
nft delete rule inet fw4 forward handle "$handle"
done
# delete started flag
rm "$STARTED_FLAG_PATH" > /dev/null 2>&1
# revert fix compatible with dockerd
## kmod-br-netfilter
if rm "$BRIDGE_NF_CALL_IPTABLES_FLAG_PATH" > /dev/null 2>&1; then
sysctl -q -w net.bridge.bridge-nf-call-iptables=1
fi
if rm "$BRIDGE_NF_CALL_IP6TABLES_FLAG_PATH" > /dev/null 2>&1; then
sysctl -q -w net.bridge.bridge-nf-call-ip6tables=1
fi
# delete cron
sed -i "/#momo/d" "/etc/crontabs/root" > /dev/null 2>&1
/etc/init.d/cron restart
}
clear_logs() {
# load config
config_load momo
# get config
## log config
local log_scheduled_clear_size_limit log_scheduled_clear_size_limit_unit
config_get log_scheduled_clear_size_limit "log" "scheduled_clear_size_limit" 1
config_get log_scheduled_clear_size_limit_unit "log" "scheduled_clear_size_limit_unit" "MB"
# prepare config
local log_scheduled_clear_size_limit_in_bytes; log_scheduled_clear_size_limit_in_bytes=0
case "$log_scheduled_clear_size_limit_unit" in
B)
log_scheduled_clear_size_limit_in_bytes=$((log_scheduled_clear_size_limit))
;;
KB)
log_scheduled_clear_size_limit_in_bytes=$((log_scheduled_clear_size_limit * 1024))
;;
MB)
log_scheduled_clear_size_limit_in_bytes=$((log_scheduled_clear_size_limit * 1024 * 1024))
;;
GB)
log_scheduled_clear_size_limit_in_bytes=$((log_scheduled_clear_size_limit * 1024 * 1024 * 1024))
;;
esac
# clear logs
if [ -f "$APP_LOG_PATH" ] && [ "$(wc -c < "$APP_LOG_PATH")" -ge "$log_scheduled_clear_size_limit_in_bytes" ]; then
echo -n > "$APP_LOG_PATH"
log "Log" "App log is cleared due to scheduled clear and exceeding size limits."
fi
if [ -f "$CORE_LOG_PATH" ] && [ "$(wc -c < "$CORE_LOG_PATH")" -ge "$log_scheduled_clear_size_limit_in_bytes" ]; then
echo -n > "$CORE_LOG_PATH"
log "Log" "Core log is cleared due to scheduled clear and exceeding size limits."
fi
}
update_subscription() {
local subscription_section; subscription_section="$1"
if [ -z "$subscription_section" ]; then
return
fi
# load config
config_load momo
# get subscription config
local subscription_name subscription_info_url subscription_url subscription_user_agent
config_get subscription_name "$subscription_section" "name"
config_get subscription_info_url "$subscription_section" "info_url"
config_get subscription_url "$subscription_section" "url"
config_get subscription_user_agent "$subscription_section" "user_agent"
# reset subscription info
uci_remove "momo" "$subscription_section" "expire" > /dev/null 2>&1
uci_remove "momo" "$subscription_section" "upload" > /dev/null 2>&1
uci_remove "momo" "$subscription_section" "download" > /dev/null 2>&1
uci_remove "momo" "$subscription_section" "total" > /dev/null 2>&1
uci_remove "momo" "$subscription_section" "used" > /dev/null 2>&1
uci_remove "momo" "$subscription_section" "avaliable" > /dev/null 2>&1
uci_remove "momo" "$subscription_section" "update" > /dev/null 2>&1
uci_remove "momo" "$subscription_section" "success" > /dev/null 2>&1
# update subscription
log "Profile" "Update subscription: $subscription_name."
local success
local subscription_info_header_tmpfile; subscription_info_header_tmpfile="$TEMP_DIR/$subscription_section.info.header"
local subscription_header_tmpfile; subscription_header_tmpfile="$TEMP_DIR/$subscription_section.header"
local subscription_tmpfile; subscription_tmpfile="$TEMP_DIR/$subscription_section.json"
local subscription_info_file
local subscription_file; subscription_file="$SUBSCRIPTIONS_DIR/$subscription_section.json"
# fetch subscription info
if [ -n "$subscription_info_url" ]; then
log "Profile" "Fetch subscription info."
if curl -s -f -m 120 --connect-timeout 15 --retry 3 -L -X GET -A "$subscription_user_agent" -D "$subscription_info_header_tmpfile" "$subscription_info_url" > /dev/null 2>&1; then
if [ ! -f "$subscription_info_file" ] && grep -q -i "subscription-userinfo:" "$subscription_info_header_tmpfile"; then
subscription_info_file="$subscription_info_header_tmpfile"
fi
fi
fi
# download subscription
log "Profile" "Download subscription."
if curl -s -f -m 120 --connect-timeout 15 --retry 3 -L -X GET -A "$subscription_user_agent" -D "$subscription_header_tmpfile" -o "$subscription_tmpfile" "$subscription_url" > /dev/null 2>&1; then
log "Profile" "Subscription download successful."
if [ ! -f "$subscription_info_file" ] && grep -q -i "subscription-userinfo:" "$subscription_header_tmpfile"; then
subscription_info_file="$subscription_header_tmpfile"
fi
log "Profile" "Subscription download successful."
success=1
else
log "Profile" "Subscription download failed."
success=0
fi
# check if success
if [ "$success" = 1 ]; then
log "Profile" "Subscription update successful."
if [ -f "$subscription_info_file" ]; then
local subscription_expire subscription_upload subscription_download subscription_total subscription_used subscription_avaliable
subscription_expire=$(grep -i "subscription-userinfo: " "$subscription_info_file" | grep -i -o -E "expire=[[:digit:]]+" | cut -d '=' -f 2)
subscription_upload=$(grep -i "subscription-userinfo: " "$subscription_info_file" | grep -i -o -E "upload=[[:digit:]]+" | cut -d '=' -f 2)
subscription_download=$(grep -i "subscription-userinfo: " "$subscription_info_file" | grep -i -o -E "download=[[:digit:]]+" | cut -d '=' -f 2)
subscription_total=$(grep -i "subscription-userinfo: " "$subscription_info_file" | grep -i -o -E "total=[[:digit:]]+" | cut -d '=' -f 2)
if [ -n "$subscription_upload" ] && [ -n "$subscription_download" ]; then
subscription_used=$((subscription_upload + subscription_download))
if [ -n "$subscription_total" ]; then
subscription_avaliable=$((subscription_total - subscription_used))
fi
fi
# update subscription info
if [ -n "$subscription_expire" ]; then
uci_set "momo" "$subscription_section" "expire" "$(date "+%Y-%m-%d %H:%M:%S" -d "@$subscription_expire")"
fi
if [ -n "$subscription_upload" ]; then
uci_set "momo" "$subscription_section" "upload" "$(format_filesize "$subscription_upload")"
fi
if [ -n "$subscription_download" ]; then
uci_set "momo" "$subscription_section" "download" "$(format_filesize "$subscription_download")"
fi
if [ -n "$subscription_total" ]; then
uci_set "momo" "$subscription_section" "total" "$(format_filesize "$subscription_total")"
fi
if [ -n "$subscription_used" ]; then
uci_set "momo" "$subscription_section" "used" "$(format_filesize "$subscription_used")"
fi
if [ -n "$subscription_avaliable" ]; then
uci_set "momo" "$subscription_section" "avaliable" "$(format_filesize "$subscription_avaliable")"
fi
fi
uci_set "momo" "$subscription_section" "update" "$(date "+%Y-%m-%d %H:%M:%S")"
uci_set "momo" "$subscription_section" "success" "1"
# update subscription file
rm -f "$subscription_info_header_tmpfile"
rm -f "$subscription_header_tmpfile"
mv -f "$subscription_tmpfile" "$subscription_file"
elif [ "$success" = 0 ]; then
log "Profile" "Subscription update failed."
# update subscription info
uci_set "momo" "$subscription_section" "success" "0"
# remove tmpfile
rm -f "$subscription_info_header_tmpfile"
rm -f "$subscription_header_tmpfile"
rm -f "$subscription_tmpfile"
fi
uci_commit "momo"
}
+2
View File
@@ -0,0 +1,2 @@
/etc/momo/profiles/
/etc/momo/subscriptions/
+343
View File
@@ -0,0 +1,343 @@
#!/bin/sh
. "$IPKG_INSTROOT/etc/momo/scripts/include.sh"
enabled=`uci get momo.config.enabled`
if [ "$enabled" == "0" ]; then
uci set momo.config.enabled=1
uci commit momo
/etc/init.d/momo restart
fi
echo \
"
# Momo Debug Info
## system
\`\`\`shell
`
cat /etc/openwrt_release
`
\`\`\`
## kernel
\`\`\`
`
uname -a
`
\`\`\`
## application
\`\`\`
`
if [ -x "/bin/opkg" ]; then
opkg list-installed "momo"
opkg list-installed "luci-app-momo"
elif [ -x "/usr/bin/apk" ]; then
apk list -I "momo"
apk list -I "luci-app-momo"
fi
`
\`\`\`
## config
\`\`\`json
`
ucode -S -e '
import { cursor } from "uci";
const uci = cursor();
const config = uci.get_all("momo");
const result = {};
for (let section_id in config) {
const section = config[section_id];
const section_type = section[".type"];
if (result[section_type] == null) {
result[section_type] = [];
}
push(result[section_type], section);
}
for (let section_type in result) {
for (let section in result[section_type]) {
delete section[".anonymous"];
delete section[".type"];
delete section[".name"];
delete section[".index"];
}
}
if (exists(result, "subscription")) {
for (let x in result["subscription"]) {
if (exists(x, "url")) {
x["url"] = "*";
}
}
}
if (exists(result, "lan_access_control")) {
for (let x in result["lan_access_control"]) {
if (exists(x, "ip")) {
for (let i = 0; i < length(x["ip"]); i++) {
x["ip"][i] = "*";
}
}
if (exists(x, "ip6")) {
for (let i = 0; i < length(x["ip6"]); i++) {
x["ip6"][i] = "*";
}
}
if (exists(x, "mac")) {
for (let i = 0; i < length(x["mac"]); i++) {
x["mac"][i] = "*";
}
}
}
}
delete result["placeholder"];
print(result);
'
`
\`\`\`
## profile
\`\`\`json
`
ucode -S -e '
import { readfile } from "fs";
function desensitize_inbounds(inbounds) {
for (let x in inbounds) {
if (exists(x, "password")) {
x["password"] = "*";
}
if (exists(x, "users")) {
x["users"] = "*";
}
if (exists(x, "obfs")) {
if (exists(x["obfs"], "password")) {
x["obfs"]["password"] = "*";
}
}
if (exists(x, "tls")) {
if (exists(x["tls"], "server_name")) {
x["tls"]["server_name"] = "*";
}
if (exists(x["tls"], "certificate")) {
x["tls"]["certificate"] = "*";
}
if (exists(x["tls"], "key")) {
x["tls"]["key"] = "*";
}
if (exists(x["tls"], "acme")) {
x["tls"]["acme"] = {};
}
if (exists(x["tls"], "reality")) {
if (exists(x["tls"]["reality"], "private_key")) {
x["tls"]["reality"]["private_key"] = "*";
}
if (exists(x["tls"]["reality"], "short_id")) {
x["tls"]["reality"]["short_id"] = "*";
}
}
}
if (exists(x, "transport")) {
if (exists(x["transport"], "host")) {
x["transport"]["host"] = "*";
}
if (exists(x["transport"], "path")) {
x["transport"]["path"] = "*";
}
if (exists(x["transport"], "server_name")) {
x["transport"]["server_name"] = "*";
}
}
}
}
function desensitize_outbounds(outbounds) {
for (let x in outbounds) {
if (exists(x, "server")) {
x["server"] = "*";
}
if (exists(x, "server_port")) {
x["server_port"] = "*";
}
if (exists(x, "server_ports")) {
x["server_port"] = "*";
}
if (exists(x, "sni")) {
x["sni"] = "*";
}
if (exists(x, "ports")) {
x["ports"] = "*";
}
if (exists(x, "port-range")) {
x["port-range"] = "*";
}
if (exists(x, "uuid")) {
x["uuid"] = "*";
}
if (exists(x, "private-key")) {
x["private-key"] = "*";
}
if (exists(x, "public-key")) {
x["public-key"] = "*";
}
if (exists(x, "token")) {
x["token"] = "*";
}
if (exists(x, "user")) {
x["user"] = "*";
}
if (exists(x, "username")) {
x["username"] = "*";
}
if (exists(x, "password")) {
x["password"] = "*";
}
if (exists(x, "obfs")) {
if (exists(x["obfs"], "password")) {
x["obfs"]["password"] = "*";
}
}
if (exists(x, "tls")) {
if (exists(x["tls"], "server_name")) {
x["tls"]["server_name"] = "*";
}
if (exists(x["tls"], "certificate")) {
x["tls"]["certificate"] = "*";
}
if (exists(x["tls"], "reality")) {
if (exists(x["tls"]["reality"], "public_key")) {
x["tls"]["reality"]["public_key"] = "*";
}
if (exists(x["tls"]["reality"], "short_id")) {
x["tls"]["reality"]["short_id"] = "*";
}
}
}
if (exists(x, "transport")) {
if (exists(x["transport"], "host")) {
x["transport"]["host"] = "*";
}
if (exists(x["transport"], "path")) {
x["transport"]["path"] = "*";
}
if (exists(x["transport"], "server_name")) {
x["transport"]["server_name"] = "*";
}
}
}
}
function desensitize_endpoints(endpoints) {
for (let x in endpoints) {
if (exists(x, "private_key")) {
x["private_key"] = "*";
}
if (exists(x, "peers")) {
for (let y in x["peers"]) {
if (exists(y, "address")) {
y["address"] = "*";
}
if (exists(y, "port")) {
y["port"] = "*";
}
if (exists(y, "public_key")) {
y["public_key"] = "*";
}
if (exists(y, "pre_shared_key")) {
y["pre_shared_key"] = "*";
}
}
}
if (exists(x, "auth_key")) {
x["auth_key"] = "*";
}
if (exists(x, "control_url")) {
x["control_url"] = "*";
}
}
}
function desensitize_profile() {
const profile = json(readfile("/etc/momo/run/config.json"));
if (exists(profile, "experimental") && exists(profile["experimental"], "clash_api") && exists(profile["experimental"]["clash_api"], "secret")) {
profile["secret"] = "*";
}
if (exists(profile, "inbounds")) {
desensitize_inbounds(profile["inbounds"]);
}
if (exists(profile, "outbounds")) {
desensitize_outbounds(profile["outbounds"]);
}
if (exists(profile, "endpoints")) {
desensitize_endpoints(profile["endpoints"]);
}
if (exists(profile, "services")) {
profile["services"] = [];
}
return profile;
}
print(desensitize_profile());
'
`
\`\`\`
## ip rule
\`\`\`
`
ip rule list
`
\`\`\`
## ip route
\`\`\`
TPROXY:
`
ip route list table "$(uci get momo.routing.tproxy_route_table)"
`
TUN:
`
ip route list table "$(uci get momo.routing.tun_route_table)"
`
\`\`\`
## ip6 rule
\`\`\`
`
ip -6 rule list
`
\`\`\`
## ip6 route
\`\`\`
TPROXY:
`
ip -6 route list table "$(uci get momo.routing.tproxy_route_table)"
`
TUN:
`
ip -6 route list table "$(uci get momo.routing.tun_route_table)"
`
\`\`\`
## firewall tables
\`\`\`
`
nft list tables
`
\`\`\`
## firewall
\`\`\`
`
nft list table inet momo
`
\`\`\`
## service
\`\`\`json
`
/etc/init.d/momo info
`
\`\`\`
"
if [ "$enabled" == "0" ]; then
uci set momo.config.enabled=0
uci commit momo
/etc/init.d/momo restart
fi
+23
View File
@@ -0,0 +1,23 @@
#!/bin/sh
. "$IPKG_INSTROOT/lib/functions.sh"
. "$IPKG_INSTROOT/etc/momo/scripts/include.sh"
config_load momo
config_get_bool enabled "config" "enabled" 0
config_get_bool core_only "config" "core_only" 0
config_get_bool proxy_enabled "proxy" "enabled" 0
config_get tcp_mode "proxy" "tcp_mode"
config_get udp_mode "proxy" "udp_mode"
config_get tun_inbound_tag "core" "tun_inbound_tag"
if [ "$enabled" = 1 ] && [ "$core_only" = 0 ] && [ "$proxy_enabled" = 1 ]; then
if [ "$tcp_mode" = "tun" ] || [ "$udp_mode" = "tun" ]; then
tun_device=$(jsonfilter -q -i "$RUN_PROFILE_PATH" -e "@['inbounds'][*]" | jsonfilter -q -a -e "@[@['tag']='$tun_inbound_tag']" | jsonfilter -q -a -e "@[@['type']='tun']" | jsonfilter -q -e "@['interface_name']")
nft insert rule inet fw4 input iifname "$tun_device" counter accept comment "momo"
nft insert rule inet fw4 forward oifname "$tun_device" counter accept comment "momo"
nft insert rule inet fw4 forward iifname "$tun_device" counter accept comment "momo"
fi
fi
exit 0
+128
View File
@@ -0,0 +1,128 @@
#!/bin/sh
. "$IPKG_INSTROOT/usr/share/libubox/jshn.sh"
# paths
## home
HOME_DIR="/etc/momo"
PROFILES_DIR="$HOME_DIR/profiles"
SUBSCRIPTIONS_DIR="$HOME_DIR/subscriptions"
## run
RUN_DIR="$HOME_DIR/run"
RUN_PROFILE_PATH="$RUN_DIR/config.json"
## ucode
UCODE_DIR="$HOME_DIR/ucode"
INCLUDE_UC="$UCODE_DIR/include.uc"
MIXIN_UC="$UCODE_DIR/mixin.uc"
HIJACK_UT="$UCODE_DIR/hijack.ut"
## scripts
SH_DIR="$HOME_DIR/scripts"
INCLUDE_SH="$SH_DIR/include.sh"
FIREWALL_INCLUDE_SH="$SH_DIR/firewall_include.sh"
DEBUG_SH="$SH_DIR/debug.sh"
## firewall
NFT_DIR="$HOME_DIR/firewall"
GEOIP_CN_NFT="$NFT_DIR/geoip_cn.nft"
GEOIP6_CN_NFT="$NFT_DIR/geoip6_cn.nft"
## log
LOG_DIR="/var/log/momo"
APP_LOG_PATH="$LOG_DIR/app.log"
CORE_LOG_PATH="$LOG_DIR/core.log"
DEBUG_LOG_PATH="$LOG_DIR/debug.log"
## temp
TEMP_DIR="/var/run/momo"
PID_FILE_PATH="$TEMP_DIR/momo.pid"
STARTED_FLAG_PATH="$TEMP_DIR/started.flag"
BRIDGE_NF_CALL_IPTABLES_FLAG_PATH="$TEMP_DIR/bridge_nf_call_iptables.flag"
BRIDGE_NF_CALL_IP6TABLES_FLAG_PATH="$TEMP_DIR/bridge_nf_call_ip6tables.flag"
# functions
get_paths() {
json_init
json_add_string home_dir "$HOME_DIR"
json_add_string profiles_dir "$PROFILES_DIR"
json_add_string subscriptions_dir "$SUBSCRIPTIONS_DIR"
json_add_string run_dir "$RUN_DIR"
json_add_string run_profile_path "$RUN_PROFILE_PATH"
json_add_string ucode_dir "$UCODE_DIR"
json_add_string include_uc "$INCLUDE_UC"
json_add_string hijack_ut "$HIJACK_UT"
json_add_string sh_dir "$SH_DIR"
json_add_string include_sh "$INCLUDE_SH"
json_add_string firewall_include_sh "$FIREWALL_INCLUDE_SH"
json_add_string debug_sh "$DEBUG_SH"
json_add_string nft_dir "$NFT_DIR"
json_add_string geoip_cn_nft "$GEOIP_CN_NFT"
json_add_string geoip6_cn_nft "$GEOIP6_CN_NFT"
json_add_string log_dir "$LOG_DIR"
json_add_string app_log_path "$APP_LOG_PATH"
json_add_string core_log_path "$CORE_LOG_PATH"
json_add_string debug_log_path "$DEBUG_LOG_PATH"
json_add_string temp_dir "$TEMP_DIR"
json_add_string pid_file_path "$PID_FILE_PATH"
json_add_string started_flag_path "$STARTED_FLAG_PATH"
json_add_string bridge_nf_call_iptables_flag_path "$BRIDGE_NF_CALL_IPTABLES_FLAG_PATH"
json_add_string bridge_nf_call_ip6tables_flag_path "$BRIDGE_NF_CALL_IP6TABLES_FLAG_PATH"
json_dump
json_cleanup
}
format_filesize() {
local b; b=1
local kb; kb=$((b * 1024))
local mb; mb=$((kb * 1024))
local gb; gb=$((mb * 1024))
local tb; tb=$((gb * 1024))
local pb; pb=$((tb * 1024))
local size; size=$1
if [ -n "$size" ]; then
if [ "$size" -lt "$kb" ]; then
echo "$(awk "BEGIN {print $size / $b}") B"
elif [ "$size" -lt "$mb" ]; then
echo "$(awk "BEGIN {print $size / $kb}") KB"
elif [ "$size" -lt "$gb" ]; then
echo "$(awk "BEGIN {print $size / $mb}") MB"
elif [ "$size" -lt "$tb" ]; then
echo "$(awk "BEGIN {print $size / $gb}") GB"
elif [ "$size" -lt "$pb" ]; then
echo "$(awk "BEGIN {print $size / $tb}") TB"
else
echo "$(awk "BEGIN {print $size / $pb}") PB"
fi
fi
}
prepare_files() {
if [ ! -d "$LOG_DIR" ]; then
mkdir -p "$LOG_DIR"
fi
if [ ! -f "$APP_LOG_PATH" ]; then
touch "$APP_LOG_PATH"
fi
if [ ! -f "$CORE_LOG_PATH" ]; then
touch "$CORE_LOG_PATH"
fi
if [ ! -d "$TEMP_DIR" ]; then
mkdir -p "$TEMP_DIR"
fi
}
log() {
echo "[$(date "+%Y-%m-%d %H:%M:%S")] [$1] $2" >> "$APP_LOG_PATH"
}
+12
View File
@@ -0,0 +1,12 @@
#!/bin/sh
. "$IPKG_INSTROOT/etc/momo/scripts/include.sh"
uci -q batch <<-EOF > /dev/null
del firewall.momo
set firewall.momo=include
set firewall.momo.type=script
set firewall.momo.path=$FIREWALL_INCLUDE_SH
set firewall.momo.fw4_compatible=1
commit firewall
EOF
+21
View File
@@ -0,0 +1,21 @@
#!/bin/sh
. "$IPKG_INSTROOT/etc/momo/scripts/include.sh"
# check momo.config.init
init=$(uci -q get momo.config.init); [ -z "$init" ] && return
# generate random string for api secret and authentication password
random=$(awk 'BEGIN{srand(); printf "%06d", int(rand() * 1000000)}')
# set momo.mixin.api_secret
uci set momo.mixin.external_control_api_secret="$random"
# remove momo.config.init
uci del momo.config.init
# commit
uci commit momo
# exit with 0
exit 0
+58
View File
@@ -0,0 +1,58 @@
#!/bin/sh
. "$IPKG_INSTROOT/etc/momo/scripts/include.sh"
# since v1.0.2
section_placeholder=$(uci -q get momo.placeholder); [ -z "$section_placeholder" ] && uci set momo.placeholder="placeholder"
# since v1.1.0
proxy_bypass_china_mainland_ip=$(uci -q get momo.proxy.bypass_china_mainland_ip)
proxy_bypass_china_mainland_ip6=$(uci -q get momo.proxy.bypass_china_mainland_ip6)
[ -z "$proxy_bypass_china_mainland_ip6" ] && uci set momo.proxy.bypass_china_mainland_ip6=$proxy_bypass_china_mainland_ip
routing_tproxy_fw_mask=$(uci -q get momo.routing.tproxy_fw_mask); [ -z "$routing_tproxy_fw_mask" ] && uci set momo.routing.tproxy_fw_mask=0xFF
routing_tun_fw_mask=$(uci -q get momo.routing.tun_fw_mask); [ -z "$routing_tun_fw_mask" ] && uci set momo.routing.tun_fw_mask=0xFF
procd=$(uci -q get momo.procd); [ -z "$procd" ] && {
uci set momo.procd=procd
uci set momo.procd.fast_reload=$(uci -q get momo.config.fast_reload)
uci del momo.config.fast_reload
}
# since v1.1.1
dummy_device=$(uci -q get momo.routing.dummy_device); [ -z "$dummy_device" ] && uci set momo.routing.dummy_device=momo-dummy
# since v1.1.2
config_scheduled_restart_cron=$(uci -q get momo.config.scheduled_restart_cron); [ -z "$config_scheduled_restart_cron" ] && uci rename momo.config.cron_expression="scheduled_restart_cron"
section_log=$(uci -q get momo.log); [ -z "$section_log" ] && {
uci set momo.log=log
uci set momo.log.scheduled_clear=1
uci set momo.log.scheduled_clear_cron="*/5 * * * *"
uci set momo.log.scheduled_clear_size_limit=1
uci set momo.log.scheduled_clear_size_limit_unit=MB
}
section_mixin=$(uci -q get momo.mixin); [ -z "$section_mixin" ] && {
uci set momo.mixin=mixin
uci set momo.mixin.log_disabled='0'
uci set momo.mixin.log_level='info'
uci set momo.mixin.log_timestamp='1'
uci set momo.mixin.dns_independent_cache='1'
uci set momo.mixin.dns_reverse_mapping='1'
uci set momo.mixin.cache_enabled='1'
uci set momo.mixin.cache_store_fakeip='1'
uci set momo.mixin.cache_store_rdrc='1'
uci set momo.mixin.external_control_ui_path='ui'
uci set momo.mixin.external_control_ui_download_url='https://github.com/Zephyruso/zashboard/releases/latest/download/dist-cdn-fonts.zip'
}
# commit
uci commit momo
# exit with 0
exit 0
+614
View File
@@ -0,0 +1,614 @@
#!/usr/bin/utpl
{%-
'use strict';
import { readfile } from 'fs';
import { cursor } from 'uci';
import { connect } from 'ubus';
import { get_paths, uci_bool, uci_array, get_cgroups_version, get_users, get_groups, get_cgroups } from '/etc/momo/ucode/include.uc';
const fw4 = require('fw4');
const paths = get_paths();
const cgroups_version = get_cgroups_version();
const users = get_users();
const groups = get_groups();
const cgroups = get_cgroups();
const uci = cursor();
const ubus = connect();
const profile = json(readfile(paths.run_profile_path));
uci.load('momo');
let redir_port;
let tproxy_port;
let tun_device;
let dns_port;
let fake_ip_range;
let fake_ip6_range;
const redirect_inbound_tag = uci.get('momo', 'core', 'redirect_inbound_tag');
const tproxy_inbound_tag = uci.get('momo', 'core', 'tproxy_inbound_tag');
const tun_inbound_tag = uci.get('momo', 'core', 'tun_inbound_tag');
const dns_inbound_tag = uci.get('momo', 'core', 'dns_inbound_tag');
const fake_ip_dns_server_tag = uci.get('momo', 'core', 'fake_ip_dns_server_tag');
let profile_array;
profile_array = filter(profile['inbounds'], (x) => x['tag'] == redirect_inbound_tag);
if (length(profile_array) > 0) {
redir_port = profile_array[0]['listen_port'];
}
profile_array = filter(profile['inbounds'], (x) => x['tag'] == tproxy_inbound_tag);
if (length(profile_array) > 0) {
tproxy_port = profile_array[0]['listen_port'];
}
profile_array = filter(profile['inbounds'], (x) => x['tag'] == tun_inbound_tag);
if (length(profile_array) > 0) {
tun_device = profile_array[0]['interface_name'];
}
profile_array = filter(profile['inbounds'], (x) => x['tag'] == dns_inbound_tag);
if (length(profile_array) > 0) {
dns_port = profile_array[0]['listen_port'];
}
profile_array = filter(profile['dns']['servers'], (x) => x['tag'] == fake_ip_dns_server_tag);
if (length(profile_array) > 0) {
fake_ip_range = profile_array[0]['inet4_range'];
fake_ip6_range = profile_array[0]['inet6_range'];
}
const ipv4_dns_hijack = uci_bool(uci.get('momo', 'proxy', 'ipv4_dns_hijack'));
const ipv6_dns_hijack = uci_bool(uci.get('momo', 'proxy', 'ipv6_dns_hijack'));
const ipv4_proxy = uci_bool(uci.get('momo', 'proxy', 'ipv4_proxy'));
const ipv6_proxy = uci_bool(uci.get('momo', 'proxy', 'ipv6_proxy'));
const fake_ip_ping_hijack = uci_bool(uci.get('momo', 'proxy', 'fake_ip_ping_hijack'));
const tcp_mode = uci.get('momo', 'proxy', 'tcp_mode');
const udp_mode = uci.get('momo', 'proxy', 'udp_mode');
const router_proxy = uci_bool(uci.get('momo', 'proxy', 'router_proxy'));
const router_access_control = [];
uci.foreach('momo', 'router_access_control', (access_control) => {
access_control['enabled'] = uci_bool(access_control['enabled']);
access_control['user'] = filter(uci_array(access_control['user']), (x) => index(users, x) >= 0);
access_control['group'] = filter(uci_array(access_control['group']), (x) => index(groups, x) >= 0);
access_control['cgroup'] = filter(uci_array(access_control['cgroup']), (x) => index(cgroups, x) >= 0);
access_control['proxy'] = uci_bool(access_control['proxy']);
access_control['dns'] = uci_bool(access_control['dns']);
if (access_control['enabled']) {
push(router_access_control, access_control);
}
});
const lan_proxy = uci_bool(uci.get('momo', 'proxy', 'lan_proxy'));
const lan_inbound_interface = uci_array(uci.get('momo', 'proxy', 'lan_inbound_interface'));
const lan_inbound_device = [];
for (let interface in lan_inbound_interface) {
const status = ubus.call('network.interface', 'status', { 'interface': interface });
const device = status?.l3_device ?? status?.device ?? '';
if (device != '') {
push(lan_inbound_device, device);
}
}
const lan_access_control = [];
uci.foreach('momo', 'lan_access_control', (access_control) => {
access_control['enabled'] = uci_bool(access_control['enabled']);
access_control['ip'] = uci_array(access_control['ip']);
access_control['ip6'] = uci_array(access_control['ip6']);
access_control['mac'] = uci_array(access_control['mac']);
access_control['proxy'] = uci_bool(access_control['proxy']);
access_control['dns'] = uci_bool(access_control['dns']);
if (access_control['enabled']) {
push(lan_access_control, access_control);
}
});
const reserved_ip = uci_array(uci.get('momo', 'proxy', 'reserved_ip'));
const reserved_ip6 = uci_array(uci.get('momo', 'proxy', 'reserved_ip6'));
const bypass_china_mainland_ip = uci_bool(uci.get('momo', 'proxy', 'bypass_china_mainland_ip'));
const bypass_china_mainland_ip6 = uci_bool(uci.get('momo', 'proxy', 'bypass_china_mainland_ip6'));
const proxy_tcp_dport = split((uci.get('momo', 'proxy', 'proxy_tcp_dport') ?? '0-65535'), ' ');
const proxy_udp_dport = split((uci.get('momo', 'proxy', 'proxy_udp_dport') ?? '0-65535'), ' ');
const bypass_dscp = uci_array(uci.get('momo', 'proxy', 'bypass_dscp'));
const bypass_fwmark = [];
for (let fwmark in uci_array(uci.get('momo', 'proxy', 'bypass_fwmark'))) {
let mark;
let mask = '0xFFFFFFFF';
if (index(fwmark, '/') >= 0) {
const parts = split(fwmark, '/');
mark = parts[0];
mask = parts[1];
} else {
mark = fwmark;
}
push(bypass_fwmark, { mark: mark, mask: mask });
}
const cgroup_id = uci.get('momo', 'routing', 'cgroup_id') ?? '0x07250725';
const cgroup_name = uci.get('momo', 'routing', 'cgroup_name') ?? 'momo';
const tproxy_fw_mark = uci.get('momo', 'routing', 'tproxy_fw_mark') ?? '0x80';
const tproxy_fw_mask = uci.get('momo', 'routing', 'tproxy_fw_mask') ?? '0xFF';
const tproxy_fw_umask = fw4.hex(~tproxy_fw_mask & 0xFFFFFFFF);
const tun_fw_mark = uci.get('momo', 'routing', 'tun_fw_mark') ?? '0x81';
const tun_fw_mask = uci.get('momo', 'routing', 'tun_fw_mask') ?? '0xFF';
const tun_fw_umask = fw4.hex(~tun_fw_mask & 0xFFFFFFFF);
const dns_hijack_nfproto = [];
if (ipv4_dns_hijack) {
push(dns_hijack_nfproto, 'ipv4');
}
if (ipv6_dns_hijack) {
push(dns_hijack_nfproto, 'ipv6');
}
const proxy_nfproto = [];
if (ipv4_proxy) {
push(proxy_nfproto, 'ipv4');
}
if (ipv6_proxy) {
push(proxy_nfproto, 'ipv6');
}
const proxy_dport = [];
for (let port in proxy_tcp_dport) {
push(proxy_dport, `tcp . ${port}`);
}
for (let port in proxy_udp_dport) {
push(proxy_dport, `udp . ${port}`);
}
-%}
table inet momo {
set dns_hijack_nfproto {
type nf_proto
flags interval
{% if (length(dns_hijack_nfproto) > 0): %}
elements = {
{{ join(', ', dns_hijack_nfproto) }}
}
{% endif %}
}
set proxy_nfproto {
type nf_proto
flags interval
{% if (length(proxy_nfproto) > 0): %}
elements = {
{{ join(', ', proxy_nfproto) }}
}
{% endif %}
}
set reserved_ip {
type ipv4_addr
flags interval
auto-merge
{% if (length(reserved_ip) > 0): %}
elements = {
{{ join(', ', reserved_ip) }}
}
{% endif %}
}
set reserved_ip6 {
type ipv6_addr
flags interval
auto-merge
{% if (length(reserved_ip6) > 0): %}
elements = {
{{ join(', ', reserved_ip6) }}
}
{% endif %}
}
set lan_inbound_device {
type ifname
flags interval
auto-merge
{% if (length(lan_inbound_device) > 0): %}
elements = {
{{ join(', ', map(lan_inbound_device, (x) => `"${x}"`)) }}
}
{% endif %}
}
set china_ip {
type ipv4_addr
flags interval
}
set china_ip6 {
type ipv6_addr
flags interval
}
set proxy_dport {
type inet_proto . inet_service
flags interval
auto-merge
{% if (length(proxy_dport) > 0): %}
elements = {
{{ join(', ', proxy_dport) }}
}
{% endif %}
}
set bypass_dscp {
type dscp
flags interval
auto-merge
{% if (length(bypass_dscp) > 0): %}
elements = {
{{ join(', ', bypass_dscp) }}
}
{% endif %}
}
{% if (router_proxy): %}
chain router_dns_hijack {
{% for (let access_control in router_access_control): %}
{% if (length(access_control['user']) == 0 && length(access_control['group']) == 0 && length(access_control['cgroup']) == 0): %}
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 counter {% if (access_control['dns']): %} redirect to :{{ dns_port }} {% else %} return {% endif %} #
{% else %}
{% if (length(access_control['user']) > 0): %}
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } meta skuid { {{ join(', ', access_control['user']) }} } th dport 53 counter {% if (access_control['dns']): %} redirect to :{{ dns_port }} {% else %} return {% endif %} #
{% endif %}
{% if (length(access_control['group']) > 0): %}
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } meta skgid { {{ join(', ', access_control['group']) }} } th dport 53 counter {% if (access_control['dns']): %} redirect to :{{ dns_port }} {% else %} return {% endif %} #
{% endif %}
{% if (cgroups_version == 2 && length(access_control['cgroup']) > 0): %}
{% for (let cgroup in access_control['cgroup']): %}
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } socket cgroupv2 level {{ length(split(cgroup, '/')) }} "{{ cgroup }}" th dport 53 counter {% if (access_control['dns']): %} redirect to :{{ dns_port }} {% else %} return {% endif %} #
{% endfor %}
{% endif %}
{% endif %}
{% endfor %}
}
{% if (tcp_mode == 'redirect'): %}
chain router_redirect {
{% for (let access_control in router_access_control): %}
{% if (length(access_control['user']) == 0 && length(access_control['group']) == 0 && length(access_control['cgroup']) == 0): %}
meta nfproto @proxy_nfproto meta l4proto tcp counter {% if (access_control['proxy']): %} redirect to :{{ redir_port }} {% else %} return {% endif %} #
{% else %}
{% if (length(access_control['user']) > 0): %}
meta nfproto @proxy_nfproto meta l4proto tcp meta skuid { {{ join(', ', access_control['user']) }} } counter {% if (access_control['proxy']): %} redirect to :{{ redir_port }} {% else %} return {% endif %} #
{% endif %}
{% if (length(access_control['group']) > 0): %}
meta nfproto @proxy_nfproto meta l4proto tcp meta skgid { {{ join(', ', access_control['group']) }} } counter {% if (access_control['proxy']): %} redirect to :{{ redir_port }} {% else %} return {% endif %} #
{% endif %}
{% if (cgroups_version == 2 && length(access_control['cgroup']) > 0): %}
{% for (let cgroup in access_control['cgroup']): %}
meta nfproto @proxy_nfproto meta l4proto tcp socket cgroupv2 level {{ length(split(cgroup, '/')) }} "{{ cgroup }}" counter {% if (access_control['proxy']): %} redirect to :{{ redir_port }} {% else %} return {% endif %} #
{% endfor %}
{% endif %}
{% endif %}
{% endfor %}
}
{% endif %}
{% if (tcp_mode == 'tproxy' || udp_mode == 'tproxy'): %}
chain router_tproxy {
{% for (let access_control in router_access_control): %}
{% if (length(access_control['user']) == 0 && length(access_control['group']) == 0 && length(access_control['cgroup']) == 0): %}
{% if (access_control['dns']): %}
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 counter return #
{% endif %}
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } {% if (access_control['proxy']): %} meta mark set meta mark & {{ tproxy_fw_umask }} | {{ tproxy_fw_mark }} counter accept {% else %} counter return {% endif %} #
{% else %}
{% if (length(access_control['user']) > 0): %}
{% if (access_control['dns']): %}
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } meta skuid { {{ join(', ', access_control['user']) }} } th dport 53 counter return #
{% endif %}
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } meta skuid { {{ join(', ', access_control['user']) }} } {% if (access_control['proxy']): %} meta mark set meta mark & {{ tproxy_fw_umask }} | {{ tproxy_fw_mark }} counter accept {% else %} counter return {% endif %} #
{% endif %}
{% if (length(access_control['group']) > 0): %}
{% if (access_control['dns']): %}
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } meta skgid { {{ join(', ', access_control['group']) }} } th dport 53 counter return #
{% endif %}
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } meta skgid { {{ join(', ', access_control['group']) }} } {% if (access_control['proxy']): %} meta mark set meta mark & {{ tproxy_fw_umask }} | {{ tproxy_fw_mark }} counter accept {% else %} counter return {% endif %} #
{% endif %}
{% if (cgroups_version == 2 && length(access_control['cgroup']) > 0): %}
{% for (let cgroup in access_control['cgroup']): %}
{% if (access_control['dns']): %}
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } socket cgroupv2 level {{ length(split(cgroup, '/')) }} "{{ cgroup }}" th dport 53 counter return #
{% endif %}
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } socket cgroupv2 level {{ length(split(cgroup, '/')) }} "{{ cgroup }}" {% if (access_control['proxy']): %} meta mark set meta mark & {{ tproxy_fw_umask }} | {{ tproxy_fw_mark }} counter accept {% else %} counter return {% endif %} #
{% endfor %}
{% endif %}
{% endif %}
{% endfor %}
}
{% endif %}
{% if (tcp_mode == 'tun' || udp_mode == 'tun'): %}
chain router_tun {
{% for (let access_control in router_access_control): %}
{% if (length(access_control['user']) == 0 && length(access_control['group']) == 0 && length(access_control['cgroup']) == 0): %}
{% if (access_control['dns']): %}
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 counter return #
{% endif %}
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } {% if (access_control['proxy']): %} meta mark set meta mark & {{ tun_fw_umask }} | {{ tun_fw_mark }} counter accept {% else %} counter return {% endif %} #
{% else %}
{% if (length(access_control['user']) > 0): %}
{% if (access_control['dns']): %}
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } meta skuid { {{ join(', ', access_control['user']) }} } th dport 53 counter return #
{% endif %}
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } meta skuid { {{ join(', ', access_control['user']) }} } {% if (access_control['proxy']): %} meta mark set meta mark & {{ tun_fw_umask }} | {{ tun_fw_mark }} counter accept {% else %} counter return {% endif %} #
{% endif %}
{% if (length(access_control['group']) > 0): %}
{% if (access_control['dns']): %}
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } meta skgid { {{ join(', ', access_control['group']) }} } th dport 53 counter return #
{% endif %}
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } meta skgid { {{ join(', ', access_control['group']) }} } {% if (access_control['proxy']): %} meta mark set meta mark & {{ tun_fw_umask }} | {{ tun_fw_mark }} counter accept {% else %} counter return {% endif %} #
{% endif %}
{% if (cgroups_version == 2 && length(access_control['cgroup']) > 0): %}
{% for (let cgroup in access_control['cgroup']): %}
{% if (access_control['dns']): %}
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } socket cgroupv2 level {{ length(split(cgroup, '/')) }} "{{ cgroup }}" th dport 53 counter return #
{% endif %}
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } socket cgroupv2 level {{ length(split(cgroup, '/')) }} "{{ cgroup }}" {% if (access_control['proxy']): %} meta mark set meta mark & {{ tun_fw_umask }} | {{ tun_fw_mark }} counter accept {% else %} counter return {% endif %} #
{% endfor %}
{% endif %}
{% endif %}
{% endfor %}
}
{% endif %}
{% endif %}
{% if (lan_proxy): %}
chain lan_dns_hijack {
{% for (let access_control in lan_access_control): %}
{% if (length(access_control['ip']) == 0 && length(access_control['ip6']) == 0 && length(access_control['mac']) == 0): %}
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 counter {% if (access_control['dns']): %} redirect to :{{ dns_port }} {% else %} return {% endif %} #
{% else %}
{% if (length(access_control['ip']) > 0): %}
{% if (ipv4_dns_hijack): %}
meta l4proto { tcp, udp } ip saddr { {{ join(', ', access_control['ip']) }} } th dport 53 counter {% if (access_control['dns']): %} redirect to :{{ dns_port }} {% else %} return {% endif %} #
{% endif %}
{% endif %}
{% if (length(access_control['ip6']) > 0): %}
{% if (ipv6_dns_hijack): %}
meta l4proto { tcp, udp } ip6 saddr { {{ join(', ', access_control['ip6']) }} } th dport 53 counter {% if (access_control['dns']): %} redirect to :{{ dns_port }} {% else %} return {% endif %} #
{% endif %}
{% endif %}
{% if (length(access_control['mac']) > 0): %}
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } ether saddr { {{ join(', ', access_control['mac']) }} } th dport 53 counter {% if (access_control['dns']): %} redirect to :{{ dns_port }} {% else %} return {% endif %} #
{% endif %}
{% endif %}
{% endfor %}
}
{% if (tcp_mode == 'redirect'): %}
chain lan_redirect {
{% for (let access_control in lan_access_control): %}
{% if (length(access_control['ip']) == 0 && length(access_control['ip6']) == 0 && length(access_control['mac']) == 0): %}
meta nfproto @proxy_nfproto meta l4proto tcp counter {% if (access_control['proxy']): %} redirect to :{{ redir_port }} {% else %} counter return {% endif %} #
{% else %}
{% if (length(access_control['ip']) > 0): %}
{% if (ipv4_proxy): %}
meta l4proto tcp ip saddr { {{ join(', ', access_control['ip']) }} } counter {% if (access_control['proxy']): %} redirect to :{{ redir_port }} {% else %} return {% endif %} #
{% endif %}
{% endif %}
{% if (length(access_control['ip6']) > 0): %}
{% if (ipv6_proxy): %}
meta l4proto tcp ip6 saddr { {{ join(', ', access_control['ip6']) }} } counter {% if (access_control['proxy']): %} redirect to :{{ redir_port }} {% else %} return {% endif %} #
{% endif %}
{% endif %}
{% if (length(access_control['mac']) > 0): %}
meta nfproto @proxy_nfproto meta l4proto tcp ether saddr { {{ join(', ', access_control['mac']) }} } counter {% if (access_control['proxy']): %} redirect to :{{ redir_port }} {% else %} return {% endif %} #
{% endif %}
{% endif %}
{% endfor %}
}
{% endif %}
{% if (tcp_mode == 'tproxy' || udp_mode == 'tproxy'): %}
chain lan_tproxy {
{% for (let access_control in lan_access_control): %}
{% if (length(access_control['ip']) == 0 && length(access_control['ip6']) == 0 && length(access_control['mac']) == 0): %}
{% if (access_control['dns']): %}
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 counter return #
{% endif %}
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } {% if (access_control['proxy']): %} meta mark set meta mark & {{ tproxy_fw_umask }} | {{ tproxy_fw_mark }} tproxy to :{{ tproxy_port }} counter accept {% else %} counter return {% endif %} #
{% else %}
{% if (length(access_control['ip']) > 0): %}
{% if (access_control['dns']): %}
{% if (ipv4_dns_hijack): %}
meta l4proto { tcp, udp } ip saddr { {{ join(', ', access_control['ip']) }} } th dport 53 counter return #
{% endif %}
{% endif %}
{% if (ipv4_proxy): %}
meta l4proto { tcp, udp } ip saddr { {{ join(', ', access_control['ip']) }} } {% if (access_control['proxy']): %} meta mark set meta mark & {{ tproxy_fw_umask }} | {{ tproxy_fw_mark }} tproxy ip to :{{ tproxy_port }} counter accept {% else %} counter return {% endif %} #
{% endif %}
{% endif %}
{% if (length(access_control['ip6']) > 0): %}
{% if (access_control['dns']): %}
{% if (ipv6_dns_hijack): %}
meta l4proto { tcp, udp } ip6 saddr { {{ join(', ', access_control['ip6']) }} } th dport 53 counter return #
{% endif %}
{% endif %}
{% if (ipv6_proxy): %}
meta l4proto { tcp, udp } ip6 saddr { {{ join(', ', access_control['ip6']) }} } {% if (access_control['proxy']): %} meta mark set meta mark & {{ tproxy_fw_umask }} | {{ tproxy_fw_mark }} tproxy ip6 to :{{ tproxy_port }} counter accept {% else %} counter return {% endif %} #
{% endif %}
{% endif %}
{% if (length(access_control['mac']) > 0): %}
{% if (access_control['dns']): %}
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } ether saddr { {{ join(', ', access_control['mac']) }} } th dport 53 counter return #
{% endif %}
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ether saddr { {{ join(', ', access_control['mac']) }} } {% if (access_control['proxy']): %} meta mark set meta mark & {{ tproxy_fw_umask }} | {{ tproxy_fw_mark }} tproxy to :{{ tproxy_port }} counter accept {% else %} counter return {% endif %} #
{% endif %}
{% endif %}
{% endfor %}
}
{% endif %}
{% if (tcp_mode == 'tun' || udp_mode == 'tun'): %}
chain lan_tun {
{% for (let access_control in lan_access_control): %}
{% if (length(access_control['ip']) == 0 && length(access_control['ip6']) == 0 && length(access_control['mac']) == 0): %}
{% if (access_control['dns']): %}
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 counter return #
{% endif %}
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } {% if (access_control['proxy']): %} meta mark set meta mark & {{ tun_fw_umask }} | {{ tun_fw_mark }} counter accept {% else %} counter return {% endif %} #
{% else %}
{% if (length(access_control['ip']) > 0): %}
{% if (access_control['dns']): %}
{% if (ipv4_dns_hijack): %}
meta l4proto { tcp, udp } ip saddr { {{ join(', ', access_control['ip']) }} } th dport 53 counter return #
{% endif %}
{% endif %}
{% if (ipv4_proxy): %}
meta l4proto { tcp, udp } ip saddr { {{ join(', ', access_control['ip']) }} } {% if (access_control['proxy']): %} meta mark set meta mark & {{ tun_fw_umask }} | {{ tun_fw_mark }} counter accept {% else %} counter return {% endif %} #
{% endif %}
{% endif %}
{% if (length(access_control['ip6']) > 0): %}
{% if (access_control['dns']): %}
{% if (ipv6_dns_hijack): %}
meta l4proto { tcp, udp } ip6 saddr { {{ join(', ', access_control['ip6']) }} } th dport 53 counter return #
{% endif %}
{% endif %}
{% if (ipv6_proxy): %}
meta l4proto { tcp, udp } ip6 saddr { {{ join(', ', access_control['ip6']) }} } {% if (access_control['proxy']): %} meta mark set meta mark & {{ tun_fw_umask }} | {{ tun_fw_mark }} counter accept {% else %} counter return {% endif %} #
{% endif %}
{% endif %}
{% if (length(access_control['mac']) > 0): %}
{% if (access_control['dns']): %}
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } ether saddr { {{ join(', ', access_control['mac']) }} } th dport 53 counter return #
{% endif %}
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ether saddr { {{ join(', ', access_control['mac']) }} } {% if (access_control['proxy']): %} meta mark set meta mark & {{ tun_fw_umask }} | {{ tun_fw_mark }} counter accept {% else %} counter return {% endif %} #
{% endif %}
{% endif %}
{% endfor %}
}
{% endif %}
{% endif %}
{% if (router_proxy): %}
chain nat_output {
type nat hook output priority filter; policy accept;
{% if (cgroups_version == 1): %}
meta cgroup {{ cgroup_id }} counter return
{% elif (cgroups_version == 2): %}
socket cgroupv2 level 2 "services/{{ cgroup_name }}" counter return
{% endif %}
jump router_dns_hijack
{% if (tcp_mode == 'redirect'): %}
fib daddr type { local, broadcast, anycast, multicast } counter return
ct direction reply counter return
ip daddr @reserved_ip {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
ip6 daddr @reserved_ip6 {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
ip daddr @china_ip counter return
ip6 daddr @china_ip6 counter return
meta nfproto ipv4 meta l4proto . th dport != @proxy_dport {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
meta nfproto ipv6 meta l4proto . th dport != @proxy_dport {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
meta l4proto { tcp, udp } ip dscp @bypass_dscp {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
meta l4proto { tcp, udp } ip6 dscp @bypass_dscp {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
{% for (let fwmark in bypass_fwmark): %}
meta mark & {{ fwmark.mask }} == {{ fwmark.mark }} counter return
{% endfor %}
jump router_redirect
{% endif %}
{% if (fake_ip_ping_hijack): %}
{% if (fake_ip_range): %}
icmp type echo-request ip daddr {{ fake_ip_range }} counter redirect
{% endif %}
{% if (fake_ip6_range): %}
icmpv6 type echo-request ip6 daddr {{ fake_ip6_range }} counter redirect
{% endif %}
{% endif %}
}
chain mangle_output {
type route hook output priority mangle; policy accept;
{% if (cgroups_version == 1): %}
meta cgroup {{ cgroup_id }} counter return
{% elif (cgroups_version == 2): %}
socket cgroupv2 level 2 "services/{{ cgroup_name }}" counter return
{% endif %}
fib daddr type { local, broadcast, anycast, multicast } counter return
ct direction reply counter return
ip daddr @reserved_ip {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
ip6 daddr @reserved_ip6 {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
ip daddr @china_ip counter return
ip6 daddr @china_ip6 counter return
meta nfproto ipv4 meta l4proto . th dport != @proxy_dport {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
meta nfproto ipv6 meta l4proto . th dport != @proxy_dport {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
meta l4proto { tcp, udp } ip dscp @bypass_dscp {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
meta l4proto { tcp, udp } ip6 dscp @bypass_dscp {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
{% for (let fwmark in bypass_fwmark): %}
meta mark & {{ fwmark.mask }} == {{ fwmark.mark }} counter return
{% endfor %}
meta l4proto vmap { tcp: {% if (tcp_mode == 'tproxy'): %} jump router_tproxy {% elif (tcp_mode == 'tun'): %} jump router_tun {% else %} continue {% endif %}, udp: {% if (udp_mode == 'tproxy'): %} jump router_tproxy {% elif (udp_mode == 'tun'): %} jump router_tun {% else %} continue {% endif %} }
}
chain mangle_prerouting_router {
type filter hook prerouting priority mangle - 1; policy accept;
{% if (tcp_mode == 'tproxy' || udp_mode == 'tproxy'): %}
iifname lo meta l4proto { tcp, udp } meta mark & {{ tproxy_fw_mask }} == {{ tproxy_fw_mark }} tproxy to :{{ tproxy_port }} counter accept
{% endif %}
{% if (tcp_mode == 'tun' || udp_mode == 'tun'): %}
iifname "{{ tun_device }}" meta l4proto { icmp, tcp, udp } counter accept
{% endif %}
}
{% endif %}
{% if (lan_proxy): %}
chain dstnat {
type nat hook prerouting priority dstnat + 1; policy accept;
iifname @lan_inbound_device jump lan_dns_hijack
{% if (tcp_mode == 'redirect'): %}
fib daddr type { local, broadcast, anycast, multicast } counter return
ct direction reply counter return
ip daddr @reserved_ip {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
ip6 daddr @reserved_ip6 {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
ip daddr @china_ip counter return
ip6 daddr @china_ip6 counter return
meta nfproto ipv4 meta l4proto . th dport != @proxy_dport {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
meta nfproto ipv6 meta l4proto . th dport != @proxy_dport {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
meta l4proto { tcp, udp } ip dscp @bypass_dscp {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
meta l4proto { tcp, udp } ip6 dscp @bypass_dscp {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
{% for (let fwmark in bypass_fwmark): %}
meta mark & {{ fwmark.mask }} == {{ fwmark.mark }} counter return
{% endfor %}
iifname @lan_inbound_device jump lan_redirect
{% endif %}
{% if (fake_ip_ping_hijack): %}
{% if (fake_ip_range): %}
icmp type echo-request ip daddr {{ fake_ip_range }} counter redirect
{% endif %}
{% if (fake_ip6_range): %}
icmpv6 type echo-request ip6 daddr {{ fake_ip6_range }} counter redirect
{% endif %}
{% endif %}
}
chain mangle_prerouting_lan {
type filter hook prerouting priority mangle; policy accept;
fib daddr type { local, broadcast, anycast, multicast } counter return
ct direction reply counter return
ip daddr @reserved_ip {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
ip6 daddr @reserved_ip6 {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
ip daddr @china_ip counter return
ip6 daddr @china_ip6 counter return
meta nfproto ipv4 meta l4proto . th dport != @proxy_dport {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
meta nfproto ipv6 meta l4proto . th dport != @proxy_dport {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
meta l4proto { tcp, udp } ip dscp @bypass_dscp {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
meta l4proto { tcp, udp } ip6 dscp @bypass_dscp {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
{% for (let fwmark in bypass_fwmark): %}
meta mark & {{ fwmark.mask }} == {{ fwmark.mark }} counter return
{% endfor %}
iifname @lan_inbound_device meta l4proto vmap { tcp: {% if (tcp_mode == 'tproxy'): %} jump lan_tproxy {% elif (tcp_mode == 'tun'): %} jump lan_tun {% else %} continue {% endif %}, udp: {% if (udp_mode == 'tproxy'): %} jump lan_tproxy {% elif (udp_mode == 'tun'): %} jump lan_tun {% else %} continue {% endif %} }
}
{% endif %}
}
{% if (bypass_china_mainland_ip): %}
include "/etc/momo/firewall/geoip_cn.nft"
{% endif %}
{% if (bypass_china_mainland_ip6): %}
include "/etc/momo/firewall/geoip6_cn.nft"
{% endif %}
+133
View File
@@ -0,0 +1,133 @@
import { readfile, writefile, popen } from 'fs';
export function get_paths() {
let result = {};
const process = popen('. /etc/momo/scripts/include.sh && get_paths');
if (process) {
result = json(process);
process.close();
}
return result;
};
export function uci_bool(obj) {
return obj == null ? null : obj == '1';
};
export function uci_int(obj) {
return obj == null ? null : int(obj);
};
export function uci_array(obj) {
if (obj == null) {
return [];
}
if (type(obj) == 'array') {
return uniq(obj);
}
return [obj];
};
export function merge(target, ...sources) {
for (let source in sources) {
for (let key in keys(source)) {
const target_value = target[key];
const target_value_type = type(target_value);
const source_value = source[key];
const source_value_type = type(source_value);
if (target_value_type === 'object' && source_value_type === 'object') {
target[key] = merge(target_value, source_value);
} else {
target[key] = source[key];
}
}
}
return target;
};
export function merge_exists(target, ...sources) {
for (let source in sources) {
for (let key in keys(source)) {
if (exists(target, key)) {
const target_value = target[key];
const target_value_type = type(target_value);
const source_value = source[key];
const source_value_type = type(source_value);
if (target_value_type === 'object' && source_value_type === 'object') {
target[key] = merge_exists(target_value, source_value);
} else {
target[key] = source[key];
}
}
}
}
return target;
};
export function trim_all(obj) {
if (obj == null) {
return null;
}
if (type(obj) == 'string') {
if (length(obj) == 0) {
return null;
}
return obj;
}
if (type(obj) == 'array') {
if (length(obj) == 0) {
return null;
}
return obj;
}
if (type(obj) == 'object') {
const obj_keys = keys(obj);
for (let key in obj_keys) {
obj[key] = trim_all(obj[key]);
if (obj[key] == null) {
delete obj[key];
}
}
if (length(keys(obj)) == 0) {
return null;
}
return obj;
}
return obj;
};
export function get_cgroups_version() {
return system('mount | grep -q -w "^cgroup"') == 0 ? 1 : 2;
};
export function get_users() {
return map(split(readfile('/etc/passwd'), '\n'), (x) => split(x, ':')[0]);
};
export function get_groups() {
return map(split(readfile('/etc/group'), '\n'), (x) => split(x, ':')[0]);
};
export function get_cgroups() {
const result = [];
if (get_cgroups_version() == 2) {
const cgroup_path = '/sys/fs/cgroup/';
const process = popen(`find ${cgroup_path} -type d -mindepth 1`);
if (process) {
for (let line = process.read('line'); length(line); line = process.read('line')) {
push(result, substr(trim(line), length(cgroup_path)));
}
}
}
return result;
};
export function load_profile() {
const paths = get_paths();
return json(readfile(paths.run_profile_path));
};
export function save_profile(obj) {
const paths = get_paths();
return writefile(paths.run_profile_path, obj);
};
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/ucode
'use strict';
import { cursor } from 'uci';
import { uci_bool, uci_int, uci_array, merge, trim_all, load_profile, save_profile } from '/etc/momo/ucode/include.uc';
const uci = cursor();
const config = {};
config['log'] = {};
config['log']['disabled'] = uci_bool(uci.get('momo', 'mixin', 'log_disabled'));
config['log']['level'] = uci.get('momo', 'mixin', 'log_level');
config['log']['timestamp'] = uci_bool(uci.get('momo', 'mixin', 'log_timestamp'));
config['log']['output'] = uci.get('momo', 'mixin', 'log_output');
config['dns'] = {};
config['dns']['strategy'] = uci.get('momo', 'mixin', 'dns_strategy');
config['dns']['disable_cache'] = uci_bool(uci.get('momo', 'mixin', 'dns_disable_cache'));
config['dns']['disable_expire'] = uci_bool(uci.get('momo', 'mixin', 'dns_disable_expire'));
config['dns']['independent_cache'] = uci_bool(uci.get('momo', 'mixin', 'dns_independent_cache'));
config['dns']['cache_capacity'] = uci_int(uci.get('momo', 'mixin', 'dns_cache_capacity'));
config['dns']['reverse_mapping'] = uci_bool(uci.get('momo', 'mixin', 'dns_reverse_mapping'));
config['ntp'] = {};
config['ntp']['enabled'] = uci_bool(uci.get('momo', 'mixin', 'ntp_enabled'));
config['ntp']['server'] = uci.get('momo', 'mixin', 'ntp_server');
config['ntp']['server_port'] = uci_int(uci.get('momo', 'mixin', 'ntp_server_port'));
config['ntp']['interval'] = uci.get('momo', 'mixin', 'ntp_interval');
config['experimental'] = {};
config['experimental']['cache_file'] = {};
config['experimental']['cache_file']['enabled'] = uci_bool(uci.get('momo', 'mixin', 'cache_enabled'));
config['experimental']['cache_file']['path'] = uci.get('momo', 'mixin', 'cache_path');
config['experimental']['cache_file']['store_fakeip'] = uci_bool(uci.get('momo', 'mixin', 'cache_store_fakeip'));
config['experimental']['cache_file']['store_rdrc'] = uci_bool(uci.get('momo', 'mixin', 'cache_store_rdrc'));
config['experimental']['clash_api'] = {};
config['experimental']['clash_api']['external_ui'] = uci.get('momo', 'mixin', 'external_control_ui_path');
config['experimental']['clash_api']['external_ui_download_url'] = uci.get('momo', 'mixin', 'external_control_ui_download_url');
config['experimental']['clash_api']['external_controller'] = uci.get('momo', 'mixin', 'external_control_api_listen');
config['experimental']['clash_api']['secret'] = uci.get('momo', 'mixin', 'external_control_api_secret');
const profile = load_profile();
save_profile(merge(profile, trim_all(config)));