update 2025-08-20 09:20:05

This commit is contained in:
actions-user 2025-08-20 09:20:05 +08:00
parent e725853e6c
commit cc9e0b310c
17 changed files with 278 additions and 193 deletions

View File

@ -1,6 +1,6 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_VERSION:=1.23.3 PKG_VERSION:=1.24.0
LUCI_TITLE:=LuCI Support for nikki LUCI_TITLE:=LuCI Support for nikki
LUCI_DEPENDS:=+luci-base +nikki LUCI_DEPENDS:=+luci-base +nikki

View File

@ -65,8 +65,6 @@ const appLogPath = `${logDir}/app.log`;
const coreLogPath = `${logDir}/core.log`; const coreLogPath = `${logDir}/core.log`;
const debugLogPath = `${logDir}/debug.log`; const debugLogPath = `${logDir}/debug.log`;
const nftDir = `${homeDir}/nftables`; const nftDir = `${homeDir}/nftables`;
const reservedIPNFT = `${nftDir}/reserved_ip.nft`;
const reservedIP6NFT = `${nftDir}/reserved_ip6.nft`;
return baseclass.extend({ return baseclass.extend({
homeDir: homeDir, homeDir: homeDir,
@ -80,8 +78,6 @@ return baseclass.extend({
appLogPath: appLogPath, appLogPath: appLogPath,
coreLogPath: coreLogPath, coreLogPath: coreLogPath,
debugLogPath: debugLogPath, debugLogPath: debugLogPath,
reservedIPNFT: reservedIPNFT,
reservedIP6NFT: reservedIP6NFT,
status: async function () { status: async function () {
return (await callRCList('nikki'))?.nikki?.running; return (await callRCList('nikki'))?.nikki?.running;

View File

@ -47,8 +47,6 @@ return view.extend({
o.value(nikki.mixinFilePath, _('File for Mixin')); o.value(nikki.mixinFilePath, _('File for Mixin'));
o.value(nikki.runProfilePath, _('Profile for Startup')); o.value(nikki.runProfilePath, _('Profile for Startup'));
o.value(nikki.reservedIPNFT, _('File for Reserved IP'));
o.value(nikki.reservedIP6NFT, _('File for Reserved IP6'));
o.write = function (section_id, formvalue) { o.write = function (section_id, formvalue) {
return true; return true;

View File

@ -117,10 +117,9 @@ return view.extend({
o.value('https://github.com/MetaCubeX/Yacd-meta/archive/refs/heads/gh-pages.zip', 'YACD'); o.value('https://github.com/MetaCubeX/Yacd-meta/archive/refs/heads/gh-pages.zip', 'YACD');
o.value('https://github.com/MetaCubeX/Razord-meta/archive/refs/heads/gh-pages.zip', 'Razord'); o.value('https://github.com/MetaCubeX/Razord-meta/archive/refs/heads/gh-pages.zip', 'Razord');
o = s.taboption('external_control', form.Value, 'api_listen', '*' + ' ' + _('API Listen')); o = s.taboption('external_control', form.Value, 'api_listen', _('API Listen'));
o.datatype = 'ipaddrport(1)'; o.datatype = 'ipaddrport(1)';
o.placeholder = _('Unmodified'); o.placeholder = _('Unmodified');
o.rmempty = false;
o = s.taboption('external_control', form.Value, 'api_secret', _('API Secret')); o = s.taboption('external_control', form.Value, 'api_secret', _('API Secret'));
o.password = true; o.password = true;
@ -152,15 +151,13 @@ return view.extend({
o.datatype = 'port'; o.datatype = 'port';
o.placeholder = _('Unmodified'); o.placeholder = _('Unmodified');
o = s.taboption('inbound', form.Value, 'redir_port', '*' + ' ' + _('Redirect Port')); o = s.taboption('inbound', form.Value, 'redir_port', _('Redirect Port'));
o.datatype = 'port'; o.datatype = 'port';
o.placeholder = _('Unmodified'); o.placeholder = _('Unmodified');
o.rmempty = false;
o = s.taboption('inbound', form.Value, 'tproxy_port', '*' + ' ' + _('TPROXY Port')); o = s.taboption('inbound', form.Value, 'tproxy_port', _('TPROXY Port'));
o.datatype = 'port'; o.datatype = 'port';
o.placeholder = _('Unmodified'); o.placeholder = _('Unmodified');
o.rmempty = false;
o = s.taboption('inbound', form.Flag, 'authentication', _('Overwrite Authentication')); o = s.taboption('inbound', form.Flag, 'authentication', _('Overwrite Authentication'));
o.rmempty = false; o.rmempty = false;
@ -185,9 +182,14 @@ return view.extend({
s.tab('tun', _('TUN Config')); s.tab('tun', _('TUN Config'));
o = s.taboption('tun', form.Value, 'tun_device', '*' + ' ' + _('Device Name')); o = s.taboption('tun', form.ListValue, 'tun_enabled', _('Enable'));
o.optional = true;
o.placeholder = _('Unmodified');
o.value('0', _('Disable'));
o.value('1', _('Enable'));
o = s.taboption('tun', form.Value, 'tun_device', _('Device Name'));
o.placeholder = _('Unmodified'); o.placeholder = _('Unmodified');
o.rmempty = false;
o = s.taboption('tun', form.ListValue, 'tun_stack', _('Stack')); o = s.taboption('tun', form.ListValue, 'tun_stack', _('Stack'));
o.optional = true; o.optional = true;
@ -227,10 +229,15 @@ return view.extend({
s.tab('dns', _('DNS Config')); s.tab('dns', _('DNS Config'));
o = s.taboption('dns', form.Value, 'dns_listen', '*' + ' ' + _('DNS Listen')); o = s.taboption('dns', form.ListValue, 'dns_enabled', _('Enable'));
o.optional = true;
o.placeholder = _('Unmodified');
o.value('0', _('Disable'));
o.value('1', _('Enable'));
o = s.taboption('dns', form.Value, 'dns_listen', _('DNS Listen'));
o.datatype = 'ipaddrport(1)'; o.datatype = 'ipaddrport(1)';
o.placeholder = _('Unmodified'); o.placeholder = _('Unmodified');
o.rmempty = false;
o = s.taboption('dns', form.ListValue, 'dns_ipv6', 'IPv6'); o = s.taboption('dns', form.ListValue, 'dns_ipv6', 'IPv6');
o.optional = true; o.optional = true;
@ -238,15 +245,15 @@ return view.extend({
o.value('0', _('Disable')); o.value('0', _('Disable'));
o.value('1', _('Enable')); o.value('1', _('Enable'));
o = s.taboption('dns', form.ListValue, 'dns_mode', '*' + ' ' + _('DNS Mode')); o = s.taboption('dns', form.ListValue, 'dns_mode', _('DNS Mode'));
o.optional = true;
o.placeholder = _('Unmodified'); o.placeholder = _('Unmodified');
o.value('redir-host', 'Redir-Host'); o.value('redir-host', 'Redir-Host');
o.value('fake-ip', 'Fake-IP'); o.value('fake-ip', 'Fake-IP');
o = s.taboption('dns', form.Value, 'fake_ip_range', '*' + ' ' + _('Fake-IP Range')); o = s.taboption('dns', form.Value, 'fake_ip_range', _('Fake-IP Range'));
o.datatype = 'cidr4'; o.datatype = 'cidr4';
o.placeholder = _('Unmodified'); o.placeholder = _('Unmodified');
o.rmempty = false;
o = s.taboption('dns', form.Flag, 'fake_ip_filter', _('Overwrite Fake-IP Filter')); o = s.taboption('dns', form.Flag, 'fake_ip_filter', _('Overwrite Fake-IP Filter'));
o.rmempty = false; o.rmempty = false;

View File

@ -132,6 +132,7 @@ return view.extend({
so.rmempty = false; so.rmempty = false;
so = o.subsection.option(form.DynamicList, 'ip', 'IP'); so = o.subsection.option(form.DynamicList, 'ip', 'IP');
so.datatype = 'ip4addr';
for (const mac in hosts) { for (const mac in hosts) {
const host = hosts[mac]; const host = hosts[mac];
@ -142,6 +143,7 @@ return view.extend({
}; };
so = o.subsection.option(form.DynamicList, 'ip6', 'IP6'); so = o.subsection.option(form.DynamicList, 'ip6', 'IP6');
so.datatype = 'ip6addr';
for (const mac in hosts) { for (const mac in hosts) {
const host = hosts[mac]; const host = hosts[mac];
@ -152,6 +154,7 @@ return view.extend({
}; };
so = o.subsection.option(form.DynamicList, 'mac', 'MAC'); so = o.subsection.option(form.DynamicList, 'mac', 'MAC');
so.datatype = 'macaddr';
for (const mac in hosts) { for (const mac in hosts) {
const host = hosts[mac]; const host = hosts[mac];

View File

@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=nikki PKG_NAME:=nikki
PKG_VERSION:=2025.07.27 PKG_VERSION:=2025.07.27
PKG_RELEASE:=1 PKG_RELEASE:=2
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
@ -43,8 +43,6 @@ endef
define Package/nikki/conffiles define Package/nikki/conffiles
/etc/config/nikki /etc/config/nikki
/etc/nikki/mixin.yaml /etc/nikki/mixin.yaml
/etc/nikki/nftables/reserved_ip.nft
/etc/nikki/nftables/reserved_ip6.nft
endef endef
define Package/nikki/install define Package/nikki/install
@ -71,8 +69,6 @@ define Package/nikki/install
$(INSTALL_BIN) $(CURDIR)/files/scripts/firewall_include.sh $(1)/etc/nikki/scripts/firewall_include.sh $(INSTALL_BIN) $(CURDIR)/files/scripts/firewall_include.sh $(1)/etc/nikki/scripts/firewall_include.sh
$(INSTALL_BIN) $(CURDIR)/files/scripts/debug.sh $(1)/etc/nikki/scripts/debug.sh $(INSTALL_BIN) $(CURDIR)/files/scripts/debug.sh $(1)/etc/nikki/scripts/debug.sh
$(INSTALL_BIN) $(CURDIR)/files/nftables/reserved_ip.nft $(1)/etc/nikki/nftables/reserved_ip.nft
$(INSTALL_BIN) $(CURDIR)/files/nftables/reserved_ip6.nft $(1)/etc/nikki/nftables/reserved_ip6.nft
$(INSTALL_BIN) $(CURDIR)/files/nftables/geoip_cn.nft $(1)/etc/nikki/nftables/geoip_cn.nft $(INSTALL_BIN) $(CURDIR)/files/nftables/geoip_cn.nft $(1)/etc/nikki/nftables/geoip_cn.nft
$(INSTALL_BIN) $(CURDIR)/files/nftables/geoip6_cn.nft $(1)/etc/nikki/nftables/geoip6_cn.nft $(INSTALL_BIN) $(CURDIR)/files/nftables/geoip6_cn.nft $(1)/etc/nikki/nftables/geoip6_cn.nft

View File

@ -1,19 +0,0 @@
#!/usr/sbin/nft -f
table inet nikki {
set reserved_ip {
type ipv4_addr
flags interval
elements = {
0.0.0.0/8,
10.0.0.0/8,
127.0.0.0/8,
100.64.0.0/10,
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
}
}
}

View File

@ -1,23 +0,0 @@
#!/usr/sbin/nft -f
table inet nikki {
set reserved_ip6 {
type ipv6_addr
flags interval
elements = {
::/128,
::1/128,
::ffff:0:0/96,
100::/64,
64:ff9b::/96,
2001::/32,
2001:10::/28,
2001:20::/28,
2001:db8::/32,
2002::/16,
fc00::/7,
fe80::/10,
ff00::/8
}
}
}

View File

@ -11,24 +11,11 @@ config config 'config'
option 'fast_reload' '0' option 'fast_reload' '0'
option 'core_only' '0' option 'core_only' '0'
config proxy 'proxy' config env 'env'
option 'enabled' '1' option 'disable_loopback_detector' '0'
option 'tcp_mode' 'redirect' option 'disable_quic_go_gso' '0'
option 'udp_mode' 'tun' option 'disable_quic_go_ecn' '0'
option 'ipv4_dns_hijack' '1' option 'skip_system_ipv6_check' '0'
option 'ipv6_dns_hijack' '1'
option 'ipv4_proxy' '1'
option 'ipv6_proxy' '1'
option 'fake_ip_ping_hijack' '1'
option 'router_proxy' '1'
option 'lan_proxy' '1'
list 'lan_inbound_interface' 'lan'
list 'bypass_dscp' '4'
option 'bypass_china_mainland_ip' '0'
option 'proxy_tcp_dport' '0-65535'
option 'proxy_udp_dport' '0-65535'
option 'tun_timeout' '30'
option 'tun_interval' '1'
config subscription 'subscription' config subscription 'subscription'
option 'name' 'default' option 'name' 'default'
@ -74,56 +61,6 @@ config mixin 'mixin'
option 'rule_provider' '0' option 'rule_provider' '0'
option 'mixin_file_content' '0' option 'mixin_file_content' '0'
config env 'env'
option 'disable_loopback_detector' '0'
option 'disable_quic_go_gso' '0'
option 'disable_quic_go_ecn' '0'
option 'skip_system_ipv6_check' '0'
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 '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 'tun_fw_mark' '0x81'
option 'tproxy_rule_pref' '1024'
option 'tun_rule_pref' '1025'
option 'tproxy_route_table' '80'
option 'tun_route_table' '81'
option 'cgroup_id' '0x12061206'
option 'cgroup_name' 'nikki'
config authentication config authentication
option 'enabled' '1' option 'enabled' '1'
option 'username' 'nikki' option 'username' 'nikki'
@ -192,6 +129,91 @@ config sniff
list 'port' '8443' list 'port' '8443'
option 'overwrite_destination' '1' option 'overwrite_destination' '1'
config proxy 'proxy'
option 'enabled' '1'
option 'tcp_mode' 'redirect'
option 'udp_mode' 'tun'
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 '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'
list 'bypass_dscp' '4'
option 'bypass_china_mainland_ip' '0'
option 'proxy_tcp_dport' '0-65535'
option 'proxy_udp_dport' '0-65535'
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 '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 'tun_fw_mark' '0x81'
option 'tproxy_rule_pref' '1024'
option 'tun_rule_pref' '1025'
option 'tproxy_route_table' '80'
option 'tun_route_table' '81'
option 'cgroup_id' '0x12061206'
option 'cgroup_name' 'nikki'
config editor 'editor' config editor 'editor'
config log 'log' config log 'log'

View File

@ -52,6 +52,13 @@ start_service() {
config_get_bool test_profile "config" "test_profile" 0 config_get_bool test_profile "config" "test_profile" 0
config_get_bool fast_reload "config" "fast_reload" 0 config_get_bool fast_reload "config" "fast_reload" 0
config_get_bool core_only "config" "core_only" 0 config_get_bool core_only "config" "core_only" 0
## environment variable
local safe_paths disable_loopback_detector disable_quic_go_gso disable_quic_go_ecn skip_system_ipv6_check
config_get safe_paths "env" "safe_paths"
config_get_bool disable_loopback_detector "env" "disable_loopback_detector" 0
config_get_bool disable_quic_go_gso "env" "disable_quic_go_gso" 0
config_get_bool disable_quic_go_ecn "env" "disable_quic_go_ecn" 0
config_get_bool skip_system_ipv6_check "env" "skip_system_ipv6_check" 0
## mixin config ## mixin config
### overwrite ### overwrite
local overwrite_authentication overwrite_tun_dns_hijack overwrite_fake_ip_filter overwrite_hosts overwrite_dns_nameserver overwrite_dns_nameserver_policy overwrite_sniffer_sniff overwrite_sniffer_force_domain_name overwrite_sniffer_ignore_domain_name local overwrite_authentication overwrite_tun_dns_hijack overwrite_fake_ip_filter overwrite_hosts overwrite_dns_nameserver overwrite_dns_nameserver_policy overwrite_sniffer_sniff overwrite_sniffer_force_domain_name overwrite_sniffer_ignore_domain_name
@ -67,13 +74,13 @@ start_service() {
### mixin file content ### mixin file content
local mixin_file_content local mixin_file_content
config_get_bool mixin_file_content "mixin" "mixin_file_content" 0 config_get_bool mixin_file_content "mixin" "mixin_file_content" 0
## environment variable ## proxy config
local safe_paths disable_loopback_detector disable_quic_go_gso disable_quic_go_ecn skip_system_ipv6_check local proxy_enabled ipv4_dns_hijack ipv6_dns_hijack tcp_mode udp_mode
config_get safe_paths "env" "safe_paths" config_get_bool proxy_enabled "proxy" "enabled" 0
config_get_bool disable_loopback_detector "env" "disable_loopback_detector" 0 config_get_bool ipv4_dns_hijack "proxy" "ipv4_dns_hijack" 0
config_get_bool disable_quic_go_gso "env" "disable_quic_go_gso" 0 config_get_bool ipv6_dns_hijack "proxy" "ipv6_dns_hijack" 0
config_get_bool disable_quic_go_ecn "env" "disable_quic_go_ecn" 0 config_get tcp_mode "proxy" "tcp_mode"
config_get_bool skip_system_ipv6_check "env" "skip_system_ipv6_check" 0 config_get udp_mode "proxy" "udp_mode"
# get profile # get profile
local profile_type; profile_type=$(echo "$profile" | cut -d ':' -f 1) local profile_type; profile_type=$(echo "$profile" | cut -d ':' -f 1)
local profile_id; profile_id=$(echo "$profile" | cut -d ':' -f 2) local profile_id; profile_id=$(echo "$profile" | cut -d ':' -f 2)
@ -144,6 +151,43 @@ start_service() {
ucode -S "$MIXIN_UC" | yq -M -p json -o yaml | yq -M -i ea '... comments="" | . as $item ireduce ({}; . * $item ) | .proxies = .nikki-proxies + .proxies | del(.nikki-proxies) | .rules = .nikki-rules + .rules | del(.nikki-rules)' "$RUN_PROFILE_PATH" "$MIXIN_FILE_PATH" - ucode -S "$MIXIN_UC" | yq -M -p json -o yaml | yq -M -i ea '... comments="" | . as $item ireduce ({}; . * $item ) | .proxies = .nikki-proxies + .proxies | del(.nikki-proxies) | .rules = .nikki-rules + .rules | del(.nikki-rules)' "$RUN_PROFILE_PATH" "$MIXIN_FILE_PATH" -
fi fi
fi fi
# check profile
if [ "$core_only" = 0 ] && [ "$proxy_enabled" = 1 ]; then
log "Profile" "Checking..."
if [ "$ipv4_dns_hijack" = 1 ] || [ "$ipv6_dns_hijack" = 1 ]; then
if (! yq -M -e 'has("dns") and (.dns | .enable) and (.dns | has("listen"))' "$RUN_PROFILE_PATH"); then
log "Profile" "Check failed."
log "Profile" "DNS should be enabled and listen should be defined."
log "App" "Exit."
return
fi
fi
if [ "$tcp_mode" = "redirect" ]; then
if (! yq -M -e 'has("redir-port")' "$RUN_PROFILE_PATH"); then
log "Profile" "Check failed."
log "Profile" "Redirect Port should be defined."
log "App" "Exit."
return
fi
fi
if [ "$tcp_mode" = "tproxy" ] || [ "$udp_mode" = "tproxy" ]; then
if (! yq -M -e 'has("tproxy-port")' "$RUN_PROFILE_PATH"); then
log "Profile" "Check failed."
log "Profile" "TPROXY Port should be defined."
log "App" "Exit."
return
fi
fi
if [ "$tcp_mode" = "tun" ] || [ "$udp_mode" = "tun" ]; then
if (! yq -M -e 'has("tun") and (.tun | .enable) and (.tun | has("device"))' "$RUN_PROFILE_PATH"); then
log "Profile" "Check failed."
log "Profile" "TUN should be enabled and device should be defined."
log "App" "Exit."
return
fi
fi
log "Profile" "Check passed."
fi
# test profile # test profile
if [ "$test_profile" = 1 ]; then if [ "$test_profile" = 1 ]; then
log "Profile" "Testing..." log "Profile" "Testing..."
@ -204,10 +248,6 @@ service_started() {
## app config ## app config
local core_only local core_only
config_get_bool core_only "config" "core_only" 0 config_get_bool core_only "config" "core_only" 0
## mixin
### tun
local tun_device
config_get tun_device "mixin" "tun_device" "nikki"
## proxy config ## proxy config
### general ### general
local tcp_mode udp_mode ipv4_proxy ipv6_proxy tun_timeout tun_interval local tcp_mode udp_mode ipv4_proxy ipv6_proxy tun_timeout tun_interval
@ -236,6 +276,7 @@ service_started() {
if [ "$tcp_mode" = "tun" ] || [ "$udp_mode" = "tun" ]; then if [ "$tcp_mode" = "tun" ] || [ "$udp_mode" = "tun" ]; then
tun_enable=1 tun_enable=1
fi fi
local tun_device; tun_device=$(yq -M '.tun.device' "$RUN_PROFILE_PATH")
if [ "$core_only" = 0 ]; then if [ "$core_only" = 0 ]; then
# proxy # proxy
log "Proxy" "Enabled." log "Proxy" "Enabled."
@ -243,12 +284,10 @@ service_started() {
if [ "$tun_enable" = 1 ]; then if [ "$tun_enable" = 1 ]; then
log "Proxy" "Waiting for tun device online within $tun_timeout seconds..." log "Proxy" "Waiting for tun device online within $tun_timeout seconds..."
while [ "$tun_timeout" -gt 0 ]; do while [ "$tun_timeout" -gt 0 ]; do
if (ip link show dev "$tun_device" > /dev/null 2>&1); then if (ip -j link show dev "$tun_device" | jsonfilter -q -e "@[@['flags'][@='UP']]" > /dev/null 2>&1); then
if [ "$(ip -json addr show dev "$tun_device" | tun_device="$tun_device" yq -M '.[] | select(.ifname = strenv(tun_device)) | .addr_info | length')" -gt 0 ]; then
log "Proxy" "TUN device is online." log "Proxy" "TUN device is online."
break break
fi fi
fi
tun_timeout=$((tun_timeout - tun_interval)) tun_timeout=$((tun_timeout - tun_interval))
sleep "$tun_interval" sleep "$tun_interval"
done done
@ -354,11 +393,11 @@ cleanup() {
# delete hijack # delete hijack
nft delete table inet nikki > /dev/null 2>&1 nft delete table inet nikki > /dev/null 2>&1
local handles handle local handles handle
handles=$(nft --json list table inet fw4 | yq -M '.nftables[] | select(has("rule")) | .rule | select(.chain == "input" and .comment == "nikki") | .handle') handles=$(nft --json list table inet fw4 | jsonfilter -q -e "@['nftables'][*]['rule']" | jsonfilter -q -a -e "@[@['chain']='input']" | jsonfilter -q -a -e "@[@['comment']='nikki']" | jsonfilter -q -a -e "@[*]['handle']")
for handle in $handles; do for handle in $handles; do
nft delete rule inet fw4 input handle "$handle" nft delete rule inet fw4 input handle "$handle"
done done
handles=$(nft --json list table inet fw4 | yq -M '.nftables[] | select(has("rule")) | .rule | select(.chain == "forward" and .comment == "nikki") | .handle') handles=$(nft --json list table inet fw4 | jsonfilter -q -e "@['nftables'][*]['rule']" | jsonfilter -q -a -e "@[@['chain']='forward']" | jsonfilter -q -a -e "@[@['comment']='nikki']" | jsonfilter -q -a -e "@[*]['handle']")
for handle in $handles; do for handle in $handles; do
nft delete rule inet fw4 forward handle "$handle" nft delete rule inet fw4 forward handle "$handle"
done done

View File

@ -9,10 +9,10 @@ config_get_bool core_only "config" "core_only" 0
config_get_bool proxy_enabled "proxy" "enabled" 0 config_get_bool proxy_enabled "proxy" "enabled" 0
config_get tcp_mode "proxy" "tcp_mode" config_get tcp_mode "proxy" "tcp_mode"
config_get udp_mode "proxy" "udp_mode" config_get udp_mode "proxy" "udp_mode"
config_get tun_device "mixin" "tun_device"
if [ "$enabled" = 1 ] && [ "$core_only" = 0 ] && [ "$proxy_enabled" = 1 ]; then if [ "$enabled" = 1 ] && [ "$core_only" = 0 ] && [ "$proxy_enabled" = 1 ]; then
if [ "$tcp_mode" = "tun" ] || [ "$udp_mode" = "tun" ]; then if [ "$tcp_mode" = "tun" ] || [ "$udp_mode" = "tun" ]; then
tun_device=$(yq -M '.tun.device' "$RUN_PROFILE_PATH")
nft insert rule inet fw4 input iifname "$tun_device" counter accept comment "nikki" nft insert rule inet fw4 input iifname "$tun_device" counter accept comment "nikki"
nft insert rule inet fw4 forward oifname "$tun_device" counter accept comment "nikki" nft insert rule inet fw4 forward oifname "$tun_device" counter accept comment "nikki"
nft insert rule inet fw4 forward iifname "$tun_device" counter accept comment "nikki" nft insert rule inet fw4 forward iifname "$tun_device" counter accept comment "nikki"

View File

@ -36,8 +36,6 @@ FIREWALL_INCLUDE_SH="$SH_DIR/firewall_include.sh"
# nftables # nftables
NFT_DIR="$HOME_DIR/nftables" NFT_DIR="$HOME_DIR/nftables"
RESERVED_IP_NFT="$NFT_DIR/reserved_ip.nft"
RESERVED_IP6_NFT="$NFT_DIR/reserved_ip6.nft"
GEOIP_CN_NFT="$NFT_DIR/geoip_cn.nft" GEOIP_CN_NFT="$NFT_DIR/geoip_cn.nft"
GEOIP6_CN_NFT="$NFT_DIR/geoip6_cn.nft" GEOIP6_CN_NFT="$NFT_DIR/geoip6_cn.nft"

View File

@ -156,6 +156,35 @@ uci show nikki | grep -o -E 'nikki.@lan_access_control\[[[:digit:]]+\]=lan_acces
[ -z "$lan_access_control_dns" ] && uci set "$lan_access_control.dns=$lan_access_control_proxy" [ -z "$lan_access_control_dns" ] && uci set "$lan_access_control.dns=$lan_access_control_proxy"
done done
# since v1.24.0
proxy_reserved_ip=$(uci -q get nikki.proxy.reserved_ip); [ -z "$proxy_reserved_ip" ] && {
uci add_list nikki.proxy.reserved_ip=0.0.0.0/8
uci add_list nikki.proxy.reserved_ip=10.0.0.0/8
uci add_list nikki.proxy.reserved_ip=127.0.0.0/8
uci add_list nikki.proxy.reserved_ip=100.64.0.0/10
uci add_list nikki.proxy.reserved_ip=169.254.0.0/16
uci add_list nikki.proxy.reserved_ip=172.16.0.0/12
uci add_list nikki.proxy.reserved_ip=192.168.0.0/16
uci add_list nikki.proxy.reserved_ip=224.0.0.0/4
uci add_list nikki.proxy.reserved_ip=240.0.0.0/4
}
proxy_reserved_ip6=$(uci -q get nikki.proxy.reserved_ip6); [ -z "$proxy_reserved_ip6" ] && {
uci add_list nikki.proxy.reserved_ip6=::/128
uci add_list nikki.proxy.reserved_ip6=::1/128
uci add_list nikki.proxy.reserved_ip6=::ffff:0:0/96
uci add_list nikki.proxy.reserved_ip6=100::/64
uci add_list nikki.proxy.reserved_ip6=64:ff9b::/96
uci add_list nikki.proxy.reserved_ip6=2001::/32
uci add_list nikki.proxy.reserved_ip6=2001:10::/28
uci add_list nikki.proxy.reserved_ip6=2001:20::/28
uci add_list nikki.proxy.reserved_ip6=2001:db8::/32
uci add_list nikki.proxy.reserved_ip6=2002::/16
uci add_list nikki.proxy.reserved_ip6=fc00::/7
uci add_list nikki.proxy.reserved_ip6=fe80::/10
uci add_list nikki.proxy.reserved_ip6=ff00::/8
}
# commit # commit
uci commit nikki uci commit nikki

View File

@ -5,7 +5,7 @@
import { cursor } from 'uci'; import { cursor } from 'uci';
import { connect } from 'ubus'; import { connect } from 'ubus';
import { uci_bool, uci_array, get_cgroups_version, get_users, get_groups, get_cgroups } from '/etc/nikki/ucode/include.uc'; import { uci_bool, uci_array, get_cgroups_version, get_users, get_groups, get_cgroups, load_profile } from '/etc/nikki/ucode/include.uc';
const cgroups_version = get_cgroups_version(); const cgroups_version = get_cgroups_version();
@ -16,16 +16,18 @@
const uci = cursor(); const uci = cursor();
const ubus = connect(); const ubus = connect();
uci.load('nikki'); const profile = load_profile();
const redir_port = uci.get('nikki', 'mixin', 'redir_port'); const redir_port = profile['redir-port'];
const tproxy_port = uci.get('nikki', 'mixin', 'tproxy_port'); const tproxy_port = profile['tproxy-port'];
const dns_listen = uci.get('nikki', 'mixin', 'dns_listen'); const dns_listen = profile['dns']['listen'];
const dns_port = substr(dns_listen, rindex(dns_listen, ':') + 1); const dns_port = substr(dns_listen, rindex(dns_listen, ':') + 1);
const fake_ip_range = uci.get('nikki', 'mixin', 'fake_ip_range'); const fake_ip_range = profile['dns']['fake-ip-range'];
const tun_device = uci.get('nikki', 'mixin', 'tun_device'); const tun_device = profile['tun']['device'];
uci.load('nikki');
const tcp_mode = uci.get('nikki', 'proxy', 'tcp_mode'); const tcp_mode = uci.get('nikki', 'proxy', 'tcp_mode');
const udp_mode = uci.get('nikki', 'proxy', 'udp_mode'); const udp_mode = uci.get('nikki', 'proxy', 'udp_mode');
@ -68,6 +70,8 @@
push(lan_access_control, access_control); 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_dscp = uci_array(uci.get('nikki', 'proxy', 'bypass_dscp')); const bypass_dscp = uci_array(uci.get('nikki', 'proxy', 'bypass_dscp'));
const bypass_china_mainland_ip = uci_bool(uci.get('nikki', 'proxy', 'bypass_china_mainland_ip')); const bypass_china_mainland_ip = uci_bool(uci.get('nikki', 'proxy', 'bypass_china_mainland_ip'));
const proxy_tcp_dport = split((uci.get('nikki', 'proxy', 'proxy_tcp_dport') ?? '0-65535'), ' '); const proxy_tcp_dport = split((uci.get('nikki', 'proxy', 'proxy_tcp_dport') ?? '0-65535'), ' ');
@ -128,12 +132,22 @@ table inet nikki {
type ipv4_addr type ipv4_addr
flags interval flags interval
auto-merge auto-merge
{% if (length(reserved_ip) > 0): %}
elements = {
{{ join(', ', reserved_ip) }}
}
{% endif %}
} }
set reserved_ip6 { set reserved_ip6 {
type ipv6_addr type ipv6_addr
flags interval flags interval
auto-merge auto-merge
{% if (length(reserved_ip6) > 0): %}
elements = {
{{ join(', ', reserved_ip6) }}
}
{% endif %}
} }
set lan_inbound_device { set lan_inbound_device {
@ -180,6 +194,7 @@ table inet nikki {
} }
{% if (router_proxy): %} {% if (router_proxy): %}
{% if (length(dns_hijack_nfproto) > 0): %}
chain router_dns_hijack { chain router_dns_hijack {
{% for (let access_control in router_access_control): %} {% for (let access_control in router_access_control): %}
{% if (access_control['enabled']): %} {% if (access_control['enabled']): %}
@ -205,7 +220,9 @@ table inet nikki {
{% endif %} {% endif %}
{% endfor %} {% endfor %}
} }
{% endif %}
{% if (tcp_mode == 'redirect'): %}
chain router_redirect { chain router_redirect {
{% for (let access_control in router_access_control): %} {% for (let access_control in router_access_control): %}
{% if (access_control['enabled']): %} {% if (access_control['enabled']): %}
@ -231,7 +248,9 @@ table inet nikki {
{% endif %} {% endif %}
{% endfor %} {% endfor %}
} }
{% endif %}
{% if (tcp_mode == 'tproxy' || udp_mode == 'tproxy'): %}
chain router_tproxy { chain router_tproxy {
{% for (let access_control in router_access_control): %} {% for (let access_control in router_access_control): %}
{% if (access_control['enabled']): %} {% if (access_control['enabled']): %}
@ -257,7 +276,9 @@ table inet nikki {
{% endif %} {% endif %}
{% endfor %} {% endfor %}
} }
{% endif %}
{% if (tcp_mode == 'tun' || udp_mode == 'tun'): %}
chain router_tun { chain router_tun {
{% for (let access_control in router_access_control): %} {% for (let access_control in router_access_control): %}
{% if (access_control['enabled']): %} {% if (access_control['enabled']): %}
@ -284,8 +305,10 @@ table inet nikki {
{% endfor %} {% endfor %}
} }
{% endif %} {% endif %}
{% endif %}
{% if (lan_proxy): %} {% if (lan_proxy): %}
{% if (length(dns_hijack_nfproto) > 0): %}
chain lan_dns_hijack { chain lan_dns_hijack {
{% for (let access_control in lan_access_control): %} {% for (let access_control in lan_access_control): %}
{% if (access_control['enabled']): %} {% if (access_control['enabled']): %}
@ -309,7 +332,9 @@ table inet nikki {
{% endif %} {% endif %}
{% endfor %} {% endfor %}
} }
{% endif %}
{% if (tcp_mode == 'redirect'): %}
chain lan_redirect { chain lan_redirect {
{% for (let access_control in lan_access_control): %} {% for (let access_control in lan_access_control): %}
{% if (access_control['enabled']): %} {% if (access_control['enabled']): %}
@ -333,7 +358,9 @@ table inet nikki {
{% endif %} {% endif %}
{% endfor %} {% endfor %}
} }
{% endif %}
{% if (tcp_mode == 'tproxy' || udp_mode == 'tproxy'): %}
chain lan_tproxy { chain lan_tproxy {
{% for (let access_control in lan_access_control): %} {% for (let access_control in lan_access_control): %}
{% if (access_control['enabled']): %} {% if (access_control['enabled']): %}
@ -357,7 +384,9 @@ table inet nikki {
{% endif %} {% endif %}
{% endfor %} {% endfor %}
} }
{% endif %}
{% if (tcp_mode == 'tun' || udp_mode == 'tun'): %}
chain lan_tun { chain lan_tun {
{% for (let access_control in lan_access_control): %} {% for (let access_control in lan_access_control): %}
{% if (access_control['enabled']): %} {% if (access_control['enabled']): %}
@ -382,6 +411,7 @@ table inet nikki {
{% endfor %} {% endfor %}
} }
{% endif %} {% endif %}
{% endif %}
{% if (router_proxy): %} {% if (router_proxy): %}
chain nat_output { chain nat_output {
@ -399,14 +429,16 @@ table inet nikki {
ip6 daddr @reserved_ip6 counter return ip6 daddr @reserved_ip6 counter return
ip daddr @china_ip counter return ip daddr @china_ip counter return
ip6 daddr @china_ip6 counter return ip6 daddr @china_ip6 counter return
meta nfproto ipv4 meta l4proto . th dport != @proxy_dport ip daddr != {{ fake_ip_range }} 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 counter return meta nfproto ipv6 meta l4proto . th dport != @proxy_dport counter return
meta l4proto { tcp, udp } ip dscp @bypass_dscp ip daddr != {{ fake_ip_range }} 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 counter return meta l4proto { tcp, udp } ip6 dscp @bypass_dscp counter return
meta nfproto @proxy_nfproto jump router_redirect meta nfproto @proxy_nfproto jump router_redirect
{% endif %} {% endif %}
{% if (fake_ip_ping_hijack): %} {% if (fake_ip_ping_hijack): %}
ip protocol icmp icmp type echo-request ip daddr {{ fake_ip_range }} counter redirect {% if (fake_ip_range ): %}
icmp type echo-request ip daddr {{ fake_ip_range }} counter redirect
{% endif %}
{% endif %} {% endif %}
} }
@ -423,9 +455,9 @@ table inet nikki {
ip6 daddr @reserved_ip6 counter return ip6 daddr @reserved_ip6 counter return
ip daddr @china_ip counter return ip daddr @china_ip counter return
ip6 daddr @china_ip6 counter return ip6 daddr @china_ip6 counter return
meta nfproto ipv4 meta l4proto . th dport != @proxy_dport ip daddr != {{ fake_ip_range }} 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 counter return meta nfproto ipv6 meta l4proto . th dport != @proxy_dport counter return
meta l4proto { tcp, udp } ip dscp @bypass_dscp ip daddr != {{ fake_ip_range }} 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 counter return meta l4proto { tcp, udp } ip6 dscp @bypass_dscp counter return
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 counter return meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 counter return
{% if (tcp_mode == 'tproxy'): %} {% if (tcp_mode == 'tproxy'): %}
@ -462,14 +494,16 @@ table inet nikki {
ip6 daddr @reserved_ip6 counter return ip6 daddr @reserved_ip6 counter return
ip daddr @china_ip counter return ip daddr @china_ip counter return
ip6 daddr @china_ip6 counter return ip6 daddr @china_ip6 counter return
meta nfproto ipv4 meta l4proto . th dport != @proxy_dport ip daddr != {{ fake_ip_range }} 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 counter return meta nfproto ipv6 meta l4proto . th dport != @proxy_dport counter return
meta l4proto { tcp, udp } ip dscp @bypass_dscp ip daddr != {{ fake_ip_range }} 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 counter return meta l4proto { tcp, udp } ip6 dscp @bypass_dscp counter return
iifname @lan_inbound_device meta nfproto @proxy_nfproto jump lan_redirect iifname @lan_inbound_device meta nfproto @proxy_nfproto jump lan_redirect
{% endif %} {% endif %}
{% if (fake_ip_ping_hijack): %} {% if (fake_ip_ping_hijack): %}
ip protocol icmp icmp type echo-request ip daddr {{ fake_ip_range }} counter redirect {% if (fake_ip_range): %}
icmp type echo-request ip daddr {{ fake_ip_range }} counter redirect
{% endif %}
{% endif %} {% endif %}
} }
@ -481,9 +515,9 @@ table inet nikki {
ip6 daddr @reserved_ip6 counter return ip6 daddr @reserved_ip6 counter return
ip daddr @china_ip counter return ip daddr @china_ip counter return
ip6 daddr @china_ip6 counter return ip6 daddr @china_ip6 counter return
meta nfproto ipv4 meta l4proto . th dport != @proxy_dport ip daddr != {{ fake_ip_range }} 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 counter return meta nfproto ipv6 meta l4proto . th dport != @proxy_dport counter return
meta l4proto { tcp, udp } ip dscp @bypass_dscp ip daddr != {{ fake_ip_range }} 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 counter return meta l4proto { tcp, udp } ip6 dscp @bypass_dscp counter return
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 counter return meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 counter return
{% if (tcp_mode == 'tproxy'): %} {% if (tcp_mode == 'tproxy'): %}
@ -500,9 +534,6 @@ table inet nikki {
{% endif %} {% endif %}
} }
include "/etc/nikki/nftables/reserved_ip.nft"
include "/etc/nikki/nftables/reserved_ip6.nft"
{% if (bypass_china_mainland_ip): %} {% if (bypass_china_mainland_ip): %}
include "/etc/nikki/nftables/geoip_cn.nft" include "/etc/nikki/nftables/geoip_cn.nft"
include "/etc/nikki/nftables/geoip6_cn.nft" include "/etc/nikki/nftables/geoip6_cn.nft"

View File

@ -75,3 +75,13 @@ export function get_cgroups() {
} }
return result; return result;
}; };
export function load_profile() {
let result = {};
const process = popen('yq -M -p yaml -o json /etc/nikki/run/config.yaml');
if (process) {
result = json(process);
process.close();
}
return result;
};

View File

@ -51,11 +51,7 @@ if (uci_bool(uci.get('nikki', 'mixin', 'authentication'))) {
} }
config['tun'] = {}; config['tun'] = {};
if (uci.get('nikki', 'proxy', 'tcp_mode') == 'tun' || uci.get('nikki', 'proxy', 'udp_mode') == 'tun') { config['tun']['enable'] = uci_bool(uci.get('nikki', 'mixin', 'tun_enabled'));
config['tun']['enable'] = true;
config['tun']['auto-route'] = false;
config['tun']['auto-redirect'] = false;
config['tun']['auto-detect-interface'] = false;
config['tun']['device'] = uci.get('nikki', 'mixin', 'tun_device'); config['tun']['device'] = uci.get('nikki', 'mixin', 'tun_device');
config['tun']['stack'] = uci.get('nikki', 'mixin', 'tun_stack'); config['tun']['stack'] = uci.get('nikki', 'mixin', 'tun_stack');
config['tun']['mtu'] = uci_int(uci.get('nikki', 'mixin', 'tun_mtu')); config['tun']['mtu'] = uci_int(uci.get('nikki', 'mixin', 'tun_mtu'));
@ -65,12 +61,14 @@ if (uci.get('nikki', 'proxy', 'tcp_mode') == 'tun' || uci.get('nikki', 'proxy',
if (uci_bool(uci.get('nikki', 'mixin', 'tun_dns_hijack'))) { if (uci_bool(uci.get('nikki', 'mixin', 'tun_dns_hijack'))) {
config['tun']['dns-hijack'] = uci_array(uci.get('nikki', 'mixin', 'tun_dns_hijacks')); config['tun']['dns-hijack'] = uci_array(uci.get('nikki', 'mixin', 'tun_dns_hijacks'));
} }
} else { if (uci_bool(uci.get('nikki', 'proxy', 'enabled'))) {
config['tun']['enable'] = false; config['tun']['auto-route'] = false;
config['tun']['auto-redirect'] = false;
config['tun']['auto-detect-interface'] = false;
} }
config['dns'] = {}; config['dns'] = {};
config['dns']['enable'] = true; config['dns']['enable'] = uci_bool(uci.get('nikki', 'mixin', 'dns_enabled'));
config['dns']['listen'] = uci.get('nikki', 'mixin', 'dns_listen'); config['dns']['listen'] = uci.get('nikki', 'mixin', 'dns_listen');
config['dns']['ipv6'] = uci_bool(uci.get('nikki', 'mixin', 'dns_ipv6')); config['dns']['ipv6'] = uci_bool(uci.get('nikki', 'mixin', 'dns_ipv6'));
config['dns']['enhanced-mode'] = uci.get('nikki', 'mixin', 'dns_mode'); config['dns']['enhanced-mode'] = uci.get('nikki', 'mixin', 'dns_mode');