update 2026-09-26 11:07:27

This commit is contained in:
action committed 2026-09-26 11:07:27 +08:00
1 parent 7b88b273d0
commit 97ae9f68da
4898 files changed
-1712274

No files matched your search

-629
View File
@@ -1,629 +0,0 @@
#!/usr/bin/utpl
{%-
'use strict';
import { cursor } from 'uci';
import { connect } from 'ubus';
import { uci_bool, uci_array, get_cgroups_version, get_users, get_groups, get_cgroups, load_profile } from '/etc/nikki/ucode/include.uc';
const fw4 = require('fw4');
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 = load_profile();
let redir_port;
let tproxy_port;
let tun_device;
let dns_port;
let fake_ip_range;
let fake_ip6_range;
const redirect_listener_name = uci.get('nikki', 'core', 'redirect_listener_name');
const tproxy_listener_name = uci.get('nikki', 'core', 'tproxy_listener_name');
const tun_listener_name = uci.get('nikki', 'core', 'tun_listener_name');
if (profile['redir-port']) {
redir_port = profile['redir-port']
} else {
const profile_listeners = filter(profile['listeners'], (x) => x['name'] == redirect_listener_name && x['type'] === 'redir');
if (length(profile_listeners) > 0) {
redir_port = profile_listeners[0]['port'];
}
}
if (profile['tproxy-port']) {
tproxy_port = profile['tproxy-port']
} else {
const profile_listeners = filter(profile['listeners'], (x) => x['name'] == tproxy_listener_name && x['type'] === 'tproxy');
if (length(profile_listeners) > 0) {
tproxy_port = profile_listeners[0]['port'];
}
}
if (profile['tun'] && profile['tun']['enable'] && profile['tun']['device']) {
tun_device = profile['tun']['device'];
} else {
const profile_listeners = filter(profile['listeners'], (x) => x['name'] == tun_listener_name && x['type'] === 'tun');
if (length(profile_listeners) > 0) {
tun_device = profile_listeners[0]['device'];
}
}
if (profile['dns']) {
const dns_listen = profile['dns']['listen'];
const dns_listen_rindex = rindex(dns_listen, ':');
if (dns_listen_rindex >= 0 && dns_listen_rindex + 1 < length(dns_listen)) {
dns_port = substr(dns_listen, dns_listen_rindex + 1);
}
fake_ip_range = profile['dns']['fake-ip-range'];
fake_ip6_range = profile['dns']['fake-ip-range6'];
}
uci.load('nikki');
const tcp_mode = uci.get('nikki', 'proxy', 'tcp_mode');
const udp_mode = uci.get('nikki', 'proxy', 'udp_mode');
const ipv4_dns_hijack = uci_bool(uci.get('nikki', 'proxy', 'ipv4_dns_hijack'));
const ipv6_dns_hijack = uci_bool(uci.get('nikki', 'proxy', 'ipv6_dns_hijack'));
const ipv4_proxy = uci_bool(uci.get('nikki', 'proxy', 'ipv4_proxy'));
const ipv6_proxy = uci_bool(uci.get('nikki', 'proxy', 'ipv6_proxy'));
const fake_ip_ping_hijack = uci_bool(uci.get('nikki', 'proxy', 'fake_ip_ping_hijack'));
const router_proxy = uci_bool(uci.get('nikki', 'proxy', 'router_proxy'));
const router_access_control = [];
uci.foreach('nikki', '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('nikki', 'proxy', 'lan_proxy'));
const lan_inbound_interface = uci_array(uci.get('nikki', '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('nikki', '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('nikki', 'proxy', 'reserved_ip'));
const reserved_ip6 = uci_array(uci.get('nikki', 'proxy', 'reserved_ip6'));
const bypass_dscp = uci_array(uci.get('nikki', 'proxy', 'bypass_dscp'));
const bypass_fwmark = [];
for (let fwmark in uci_array(uci.get('nikki', '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 bypass_china_mainland_ip = uci_bool(uci.get('nikki', 'proxy', 'bypass_china_mainland_ip'));
const bypass_china_mainland_ip6 = uci_bool(uci.get('nikki', 'proxy', 'bypass_china_mainland_ip6'));
const proxy_tcp_dport = split((uci.get('nikki', 'proxy', 'proxy_tcp_dport') ?? '0-65535'), ' ');
const proxy_udp_dport = split((uci.get('nikki', 'proxy', 'proxy_udp_dport') ?? '0-65535'), ' ');
const cgroup_id = uci.get('nikki', 'routing', 'cgroup_id') ?? '0x12061206';
const cgroup_name = uci.get('nikki', 'routing', 'cgroup_name') ?? 'nikki';
const tproxy_fw_mark = uci.get('nikki', 'routing', 'tproxy_fw_mark') ?? '0x80';
const tproxy_fw_mask = uci.get('nikki', 'routing', 'tproxy_fw_mask') ?? '0xFF';
const tproxy_fw_umask = fw4.hex(~tproxy_fw_mask & 0xFFFFFFFF);
const tun_fw_mark = uci.get('nikki', 'routing', 'tun_fw_mark') ?? '0x81';
const tun_fw_mask = uci.get('nikki', '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 nikki {
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): %}
{% if (length(dns_hijack_nfproto) > 0): %}
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 %}
}
{% endif %}
{% 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): %}
{% if (length(dns_hijack_nfproto) > 0): %}
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 %}
}
{% endif %}
{% 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 %}
{% if (length(dns_hijack_nfproto) > 0): %}
jump router_dns_hijack
{% endif %}
{% 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;
{% if (length(dns_hijack_nfproto) > 0): %}
iifname @lan_inbound_device jump lan_dns_hijack
{% endif %}
{% 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/nikki/nftables/geoip_cn.nft"
{% endif %}
{% if (bypass_china_mainland_ip6): %}
include "/etc/nikki/nftables/geoip6_cn.nft"
{% endif %}
-87
View File
@@ -1,87 +0,0 @@
import { readfile, popen } from 'fs';
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 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() {
let result = {};
const process = popen('yq -M -p yaml -o json /etc/nikki/run/config.yaml');
if (process) {
result = json(process.read('all'));
process.close();
}
return result;
};
-204
View File
@@ -1,204 +0,0 @@
#!/usr/bin/ucode
'use strict';
import { cursor } from 'uci';
import { connect } from 'ubus';
import { uci_bool, uci_int, uci_array, trim_all } from '/etc/nikki/ucode/include.uc';
const uci = cursor();
const ubus = connect();
const config = {};
const outbound_interface = uci.get('nikki', 'mixin', 'outbound_interface');
const outbound_interface_status = ubus.call('network.interface', 'status', { 'interface': outbound_interface });
const outbound_device = outbound_interface_status?.l3_device ?? outbound_interface_status?.device ?? '';
config['log-level'] = uci.get('nikki', 'mixin', 'log_level');
config['mode'] = uci.get('nikki', 'mixin', 'mode');
config['find-process-mode'] = uci.get('nikki', 'mixin', 'match_process');
config['interface-name'] = outbound_device;
config['ipv6'] = uci_bool(uci.get('nikki', 'mixin', 'ipv6'));
config['unified-delay'] = uci_bool(uci.get('nikki', 'mixin', 'unify_delay'));
config['tcp-concurrent'] = uci_bool(uci.get('nikki', 'mixin', 'tcp_concurrent'));
config['disable-keep-alive'] = uci_bool(uci.get('nikki', 'mixin', 'disable_tcp_keep_alive'));
config['keep-alive-idle'] = uci_int(uci.get('nikki', 'mixin', 'tcp_keep_alive_idle'));
config['keep-alive-interval'] = uci_int(uci.get('nikki', 'mixin', 'tcp_keep_alive_interval'));
config['external-ui'] = uci.get('nikki', 'mixin', 'ui_path');
config['external-ui-name'] = uci.get('nikki', 'mixin', 'ui_name');
config['external-ui-url'] = uci.get('nikki', 'mixin', 'ui_url');
config['external-controller'] = uci.get('nikki', 'mixin', 'api_listen');
config['external-controller-tls'] = uci.get('nikki', 'mixin', 'api_tls_listen');
config['tls'] = {};
config['tls']['certificate'] = uci.get('nikki', 'mixin', 'api_tls_cert');
config['tls']['private-key'] = uci.get('nikki', 'mixin', 'api_tls_key');
config['tls']['ech-key'] = uci.get('nikki', 'mixin', 'api_tls_ech_key');
config['secret'] = uci.get('nikki', 'mixin', 'api_secret');
config['allow-lan'] = uci_bool(uci.get('nikki', 'mixin', 'allow_lan'));
config['port'] = uci_int(uci.get('nikki', 'mixin', 'http_port'));
config['socks-port'] = uci_int(uci.get('nikki', 'mixin', 'socks_port'));
config['mixed-port'] = uci_int(uci.get('nikki', 'mixin', 'mixed_port'));
config['redir-port'] = uci_int(uci.get('nikki', 'mixin', 'redir_port'));
config['tproxy-port'] = uci_int(uci.get('nikki', 'mixin', 'tproxy_port'));
if (uci_bool(uci.get('nikki', 'mixin', 'authentication'))) {
config['authentication'] = [];
uci.foreach('nikki', 'authentication', (section) => {
if (!uci_bool(section.enabled)) {
return;
}
push(config['authentication'], `${section.username}:${section.password}`);
});
}
config['tun'] = {};
config['tun']['enable'] = uci_bool(uci.get('nikki', 'mixin', 'tun_enabled'));
config['tun']['device'] = uci.get('nikki', 'mixin', 'tun_device');
config['tun']['stack'] = uci.get('nikki', 'mixin', 'tun_stack');
config['tun']['mtu'] = uci_int(uci.get('nikki', 'mixin', 'tun_mtu'));
config['tun']['gso'] = uci_bool(uci.get('nikki', 'mixin', 'tun_gso'));
config['tun']['gso-max-size'] = uci_int(uci.get('nikki', 'mixin', 'tun_gso_max_size'));
if (uci_bool(uci.get('nikki', 'mixin', 'tun_dns_hijack'))) {
config['tun']['dns-hijack'] = uci_array(uci.get('nikki', 'mixin', 'tun_dns_hijacks'));
}
config['dns'] = {};
config['dns']['enable'] = uci_bool(uci.get('nikki', 'mixin', 'dns_enabled'));
config['dns']['cache-algorithm'] = uci.get('nikki', 'mixin', 'dns_cache_algorithm');
config['dns']['listen'] = uci.get('nikki', 'mixin', 'dns_listen');
config['dns']['ipv6'] = uci_bool(uci.get('nikki', 'mixin', 'dns_ipv6'));
config['dns']['enhanced-mode'] = uci.get('nikki', 'mixin', 'dns_mode');
config['dns']['fake-ip-range'] = uci.get('nikki', 'mixin', 'fake_ip_range');
config['dns']['fake-ip-range6'] = uci.get('nikki', 'mixin', 'fake_ip6_range');
config['dns']['fake-ip-ttl'] = uci_int(uci.get('nikki', 'mixin', 'fake_ip_ttl'));
if (uci_bool(uci.get('nikki', 'mixin', 'fake_ip_filter'))) {
config['dns']['fake-ip-filter'] = uci_array(uci.get('nikki', 'mixin', 'fake_ip_filters'));
}
config['dns']['fake-ip-filter-mode'] = uci.get('nikki', 'mixin', 'fake_ip_filter_mode');
config['dns']['respect-rules'] = uci_bool(uci.get('nikki', 'mixin', 'dns_respect_rules'));
config['dns']['prefer-h3'] = uci_bool(uci.get('nikki', 'mixin', 'dns_doh_prefer_http3'));
config['dns']['use-system-hosts'] = uci_bool(uci.get('nikki', 'mixin', 'dns_system_hosts'));
config['dns']['use-hosts'] = uci_bool(uci.get('nikki', 'mixin', 'dns_hosts'));
if (uci_bool(uci.get('nikki', 'mixin', 'hosts'))) {
config['hosts'] = {};
uci.foreach('nikki', 'hosts', (section) => {
if (!uci_bool(section.enabled)) {
return;
}
config['hosts'][section.domain_name] = uci_array(section.ip);
});
}
if (uci_bool(uci.get('nikki', 'mixin', 'dns_nameserver'))) {
config['dns']['default-nameserver'] = [];
config['dns']['proxy-server-nameserver'] = [];
config['dns']['direct-nameserver'] = [];
config['dns']['nameserver'] = [];
config['dns']['fallback'] = [];
uci.foreach('nikki', 'nameserver', (section) => {
if (!uci_bool(section.enabled)) {
return;
}
push(config['dns'][section.type], ...uci_array(section.nameserver));
})
}
if (uci_bool(uci.get('nikki', 'mixin', 'dns_proxy_server_nameserver_policy'))) {
config['dns']['proxy-server-nameserver-policy'] = {};
uci.foreach('nikki', 'proxy_server_nameserver_policy', (section) => {
if (!uci_bool(section.enabled)) {
return;
}
config['dns']['proxy-server-nameserver-policy'][section.matcher] = uci_array(section.nameserver);
});
}
config['dns']['direct-nameserver-follow-policy'] = uci_bool(uci.get('nikki', 'mixin', 'dns_direct_nameserver_follow_policy'));
if (uci_bool(uci.get('nikki', 'mixin', 'dns_nameserver_policy'))) {
config['dns']['nameserver-policy'] = {};
uci.foreach('nikki', 'nameserver_policy', (section) => {
if (!uci_bool(section.enabled)) {
return;
}
config['dns']['nameserver-policy'][section.matcher] = uci_array(section.nameserver);
});
}
config['sniffer'] = {};
config['sniffer']['enable'] = uci_bool(uci.get('nikki', 'mixin', 'sniffer'));
config['sniffer']['force-dns-mapping'] = uci_bool(uci.get('nikki', 'mixin', 'sniffer_sniff_dns_mapping'));
config['sniffer']['parse-pure-ip'] = uci_bool(uci.get('nikki', 'mixin', 'sniffer_sniff_pure_ip'));
if (uci_bool(uci.get('nikki', 'mixin', 'sniffer_force_domain_name'))) {
config['sniffer']['force-domain'] = uci_array(uci.get('nikki', 'mixin', 'sniffer_force_domain_names'));
}
if (uci_bool(uci.get('nikki', 'mixin', 'sniffer_ignore_domain_name'))) {
config['sniffer']['skip-domain'] = uci_array(uci.get('nikki', 'mixin', 'sniffer_ignore_domain_names'));
}
if (uci_bool(uci.get('nikki', 'mixin', 'sniffer_sniff'))) {
config['sniffer']['sniff'] = {};
config['sniffer']['sniff']['HTTP'] = {};
config['sniffer']['sniff']['TLS'] = {};
config['sniffer']['sniff']['QUIC'] = {};
uci.foreach('nikki', 'sniff', (section) => {
if (!uci_bool(section.enabled)) {
return;
}
config['sniffer']['sniff'][section.protocol]['port'] = uci_array(section.port);
config['sniffer']['sniff'][section.protocol]['override-destination'] = uci_bool(section.overwrite_destination);
});
}
config['profile'] = {};
config['profile']['store-selected'] = uci_bool(uci.get('nikki', 'mixin', 'selection_cache'));
config['profile']['store-fake-ip'] = uci_bool(uci.get('nikki', 'mixin', 'fake_ip_cache'));
if (uci_bool(uci.get('nikki', 'mixin', 'rule_provider'))) {
config['rule-providers'] = {};
uci.foreach('nikki', 'rule_provider', (section) => {
if (!uci_bool(section.enabled)) {
return;
}
if (section.type == 'http') {
config['rule-providers'][section.name] = {
type: section.type,
url: section.url,
proxy: section.node,
size_limit: section.file_size_limit,
format: section.file_format,
behavior: section.behavior,
interval: section.update_interval,
}
} else if (section.type == 'file') {
config['rule-providers'][section.name] = {
type: section.type,
path: section.file_path,
format: section.file_format,
behavior: section.behavior,
}
}
})
}
if (uci_bool(uci.get('nikki', 'mixin', 'rule'))) {
config['nikki-rules'] = [];
uci.foreach('nikki', 'rule', (section) => {
if (!uci_bool(section.enabled)) {
return;
}
const rule = [ section.type, section.matcher, section.node, uci_bool(section.no_resolve) ? 'no-resolve' : null ];
push(config['nikki-rules'], join(',', filter(rule, (item) => item != null && item != '')));
})
}
const geoip_format = uci.get('nikki', 'mixin', 'geoip_format');
config['geodata-mode'] = geoip_format == null ? null : geoip_format == 'dat';
config['geodata-loader'] = uci.get('nikki', 'mixin', 'geodata_loader');
config['geox-url'] = {};
config['geox-url']['geosite'] = uci.get('nikki', 'mixin', 'geosite_url');
config['geox-url']['mmdb'] = uci.get('nikki', 'mixin', 'geoip_mmdb_url');
config['geox-url']['geoip'] = uci.get('nikki', 'mixin', 'geoip_dat_url');
config['geox-url']['asn'] = uci.get('nikki', 'mixin', 'geoip_asn_url');
config['geo-auto-update'] = uci_bool(uci.get('nikki', 'mixin', 'geox_auto_update'));
config['geo-update-interval'] = uci_int(uci.get('nikki', 'mixin', 'geox_update_interval'));
print(trim_all(config));