#!/usr/bin/utpl {%- 'use strict'; import { readfile } from 'fs'; import { cursor } from 'uci'; import { connect } from 'ubus'; import { uci_bool, uci_array } from '/etc/nikki/ucode/include.uc'; let users = map(split(readfile('/etc/passwd'), '\n'), (x) => split(x, ':')[0]); let groups = map(split(readfile('/etc/group'), '\n'), (x) => split(x, ':')[0]); const uci = cursor(); const ubus = connect(); uci.load('nikki'); const redir_port = uci.get('nikki', 'mixin', 'redir_port'); const tproxy_port = uci.get('nikki', 'mixin', 'tproxy_port'); const dns_port = uci.get('nikki', 'mixin', 'dns_port'); const fake_ip_range = uci.get('nikki', 'mixin', 'fake_ip_range'); const tun_device = uci.get('nikki', 'mixin', 'tun_device'); const tcp_transparent_proxy_mode = uci.get('nikki', 'proxy', 'tcp_transparent_proxy_mode'); const udp_transparent_proxy_mode = uci.get('nikki', 'proxy', 'udp_transparent_proxy_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 lan_proxy = uci_bool(uci.get('nikki', 'proxy', 'lan_proxy')); const access_control_mode = uci.get('nikki', 'proxy', 'access_control_mode'); const acl_ip = uci_array(uci.get('nikki', 'proxy', 'acl_ip')); const acl_ip6 = uci_array(uci.get('nikki', 'proxy', 'acl_ip6')); const acl_mac = uci_array(uci.get('nikki', 'proxy', 'acl_mac')); const acl_interface = uci_array(uci.get('nikki', 'proxy', 'acl_interface')); const bypass_user = filter(uci_array(uci.get('nikki', 'proxy', 'bypass_user')), (x) => x != "root" && index(users, x) >= 0); const bypass_group = filter(uci_array(uci.get('nikki', 'proxy', 'bypass_group')), (x) => x != "root" && index(groups, x) >= 0); 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 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 dns_hijack_nfproto = []; if (ipv4_dns_hijack) { push(dns_hijack_nfproto, 'ipv4'); } if (ipv6_dns_hijack) { push(dns_hijack_nfproto, 'ipv6'); } const acl_device = []; for (let i = 0; i < length(acl_interface); i++) { const device = ubus.call('network.interface', 'status', {'interface': acl_interface[i]})?.l3_device ?? ''; if (device != '') { push(acl_device, device); } } 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}`); } push(bypass_group, nikki_group); -%} table inet nikki { set dns_hijack_nfproto { type nf_proto flags interval {% if (length(dns_hijack_nfproto) > 0): %} elements = { {% for (let x in dns_hijack_nfproto): %} {{ x }}, {% endfor %} } {% endif %} } set proxy_nfproto { type nf_proto flags interval {% if (length(proxy_nfproto) > 0): %} elements = { {% for (let x in proxy_nfproto): %} {{ x }}, {% endfor %} } {% endif %} } set bypass_user { type uid flags interval auto-merge {% if (length(bypass_user) > 0): %} elements = { {% for (let x in bypass_user): %} {{ x }}, {% endfor %} } {% endif %} } set bypass_group { type gid flags interval auto-merge {% if (length(bypass_group) > 0): %} elements = { {% for (let x in bypass_group): %} {{ x }}, {% endfor %} } {% endif %} } set reserved_ip { type ipv4_addr flags interval auto-merge } set reserved_ip6 { type ipv6_addr flags interval auto-merge } 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 = { {% for (let x in proxy_dport): %} {{ x }}, {% endfor %} } {% endif %} } set bypass_dscp { type dscp flags interval auto-merge {% if (length(bypass_dscp) > 0): %} elements = { {% for (let x in bypass_dscp): %} {{ x }}, {% endfor %} } {% endif %} } set acl_ip { type ipv4_addr flags interval auto-merge {% if (length(acl_ip) > 0): %} elements = { {% for (let x in acl_ip): %} {{ x }}, {% endfor %} } {% endif %} } set acl_ip6 { type ipv6_addr flags interval auto-merge {% if (length(acl_ip6) > 0): %} elements = { {% for (let x in acl_ip6): %} {{ x }}, {% endfor %} } {% endif %} } set acl_mac { type ether_addr flags interval auto-merge {% if (length(acl_mac) > 0): %} elements = { {% for (let x in acl_mac): %} {{ x }}, {% endfor %} } {% endif %} } set acl_device { type ifname flags interval auto-merge {% if (length(acl_device) > 0): %} elements = { {% for (let x in acl_device): %} {{ x }}, {% endfor %} } {% endif %} } chain lan_dns_hijack { {% if (access_control_mode == 'all'): %} meta l4proto { tcp, udp } th dport 53 counter redirect to :{{ dns_port }} {% elif (access_control_mode == 'allow'): %} meta l4proto { tcp, udp } th dport 53 ip saddr @acl_ip counter redirect to :{{ dns_port }} meta l4proto { tcp, udp } th dport 53 ip6 saddr @acl_ip6 counter redirect to :{{ dns_port }} meta l4proto { tcp, udp } th dport 53 ether saddr @acl_mac counter redirect to :{{ dns_port }} meta l4proto { tcp, udp } th dport 53 iifname @acl_device counter redirect to :{{ dns_port }} {% elif (access_control_mode == 'block'): %} meta l4proto { tcp, udp } th dport 53 ip saddr @acl_ip counter return meta l4proto { tcp, udp } th dport 53 ip6 saddr @acl_ip6 counter return meta l4proto { tcp, udp } th dport 53 ether saddr @acl_mac counter return meta l4proto { tcp, udp } th dport 53 iifname @acl_device counter return meta l4proto { tcp, udp } th dport 53 counter redirect to :{{ dns_port }} {% endif %} } chain lan_redirect { {% if (access_control_mode == 'all'): %} meta l4proto tcp counter redirect to :{{ redir_port }} {% elif (access_control_mode == 'allow'): %} meta l4proto tcp ip saddr @acl_ip counter redirect to :{{ redir_port }} meta l4proto tcp ip6 saddr @acl_ip6 counter redirect to :{{ redir_port }} meta l4proto tcp ether saddr @acl_mac counter redirect to :{{ redir_port }} meta l4proto tcp iifname @acl_device counter redirect to :{{ redir_port }} {% elif (access_control_mode == 'block'): %} meta l4proto tcp ip saddr @acl_ip counter return meta l4proto tcp ip6 saddr @acl_ip6 counter return meta l4proto tcp ether saddr @acl_mac counter return meta l4proto tcp iifname @acl_device counter return meta l4proto tcp counter redirect to :{{ redir_port }} {% endif %} } chain lan_tproxy { {% if (access_control_mode == 'all'): %} meta l4proto { tcp, udp } meta mark set {{ tproxy_fw_mark }} tproxy to :{{ tproxy_port }} counter accept {% elif (access_control_mode == 'allow'): %} meta l4proto { tcp, udp } ip saddr @acl_ip meta mark set {{ tproxy_fw_mark }} tproxy ip to :{{ tproxy_port }} counter accept meta l4proto { tcp, udp } ip6 saddr @acl_ip6 meta mark set {{ tproxy_fw_mark }} tproxy ip6 to :{{ tproxy_port }} counter accept meta l4proto { tcp, udp } ether saddr @acl_mac meta mark set {{ tproxy_fw_mark }} tproxy to :{{ tproxy_port }} counter accept meta l4proto { tcp, udp } iifname @acl_device meta mark set {{ tproxy_fw_mark }} tproxy to :{{ tproxy_port }} counter accept {% elif (access_control_mode == 'block'): %} meta l4proto { tcp, udp } ip saddr @acl_ip counter return meta l4proto { tcp, udp } ip6 saddr @acl_ip6 counter return meta l4proto { tcp, udp } ether saddr @acl_mac counter return meta l4proto { tcp, udp } iifname @acl_device counter return meta l4proto { tcp, udp } meta mark set {{ tproxy_fw_mark }} tproxy to :{{ tproxy_port }} counter accept {% endif %} } chain lan_tun { {% if (access_control_mode == 'all'): %} meta l4proto { tcp, udp } meta mark set {{ tun_fw_mark }} counter {% elif (access_control_mode == 'allow'): %} meta l4proto { tcp, udp } ip saddr @acl_ip meta mark set {{ tun_fw_mark }} counter meta l4proto { tcp, udp } ip6 saddr @acl_ip6 meta mark set {{ tun_fw_mark }} counter meta l4proto { tcp, udp } ether saddr @acl_mac meta mark set {{ tun_fw_mark }} counter meta l4proto { tcp, udp } iifname @acl_device meta mark set {{ tun_fw_mark }} counter {% elif (access_control_mode == 'block'): %} meta l4proto { tcp, udp } ip saddr @acl_ip counter return meta l4proto { tcp, udp } ip6 saddr @acl_ip6 counter return meta l4proto { tcp, udp } ether saddr @acl_mac counter return meta l4proto { tcp, udp } iifname @acl_device counter return meta l4proto { tcp, udp } meta mark set {{ tun_fw_mark }} counter {% endif %} } {% if (router_proxy): %} chain nat_output { type nat hook output priority filter; policy accept; meta skuid @bypass_user counter return meta skgid @bypass_group counter return meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 counter redirect to :{{ dns_port }} {% if (tcp_transparent_proxy_mode == 'redirect'): %} fib daddr type { local, multicast, broadcast, anycast } counter return ct direction reply counter return ip daddr @reserved_ip counter return ip6 daddr @reserved_ip6 counter return ip daddr @china_ip 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 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 } ip6 dscp @bypass_dscp counter return meta nfproto @proxy_nfproto meta l4proto tcp counter redirect to :{{ redir_port }} {% endif %} {% if (fake_ip_ping_hijack): %} ip protocol icmp ip daddr {{ fake_ip_range }} counter redirect {% endif %} } chain mangle_output { type route hook output priority mangle; policy accept; meta skuid @bypass_user counter return meta skgid @bypass_group counter return fib daddr type { local, multicast, broadcast, anycast } counter return ct direction reply counter return ip daddr @reserved_ip counter return ip6 daddr @reserved_ip6 counter return ip daddr @china_ip 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 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 } ip6 dscp @bypass_dscp counter return meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 counter return {% if (tcp_transparent_proxy_mode == 'tproxy'): %} meta nfproto @proxy_nfproto meta l4proto tcp meta mark set {{ tproxy_fw_mark }} counter {% elif (tcp_transparent_proxy_mode == 'tun'): %} meta nfproto @proxy_nfproto meta l4proto tcp meta mark set {{ tun_fw_mark }} counter {% endif %} {% if (udp_transparent_proxy_mode == 'tproxy'): %} meta nfproto @proxy_nfproto meta l4proto udp meta mark set {{ tproxy_fw_mark }} counter {% elif (udp_transparent_proxy_mode == 'tun'): %} meta nfproto @proxy_nfproto meta l4proto udp meta mark set {{ tun_fw_mark }} counter {% endif %} } chain mangle_prerouting_router { type filter hook prerouting priority mangle - 1; policy accept; {% if (tcp_transparent_proxy_mode == 'tproxy' || udp_transparent_proxy_mode == 'tproxy'): %} meta l4proto { tcp, udp } iifname lo meta mark {{ tproxy_fw_mark }} tproxy to :{{ tproxy_port }} counter accept {% endif %} {% if (tcp_transparent_proxy_mode == 'tun' || udp_transparent_proxy_mode == 'tun'): %} meta l4proto { tcp, udp } iifname {{ tun_device }} counter accept {% endif %} } {% endif %} {% if (lan_proxy): %} chain dstnat { type nat hook prerouting priority dstnat + 1; policy accept; meta nfproto @dns_hijack_nfproto jump lan_dns_hijack {% if (tcp_transparent_proxy_mode == 'redirect'): %} fib daddr type { local, multicast, broadcast, anycast } counter return ct direction reply counter return ip daddr @reserved_ip counter return ip6 daddr @reserved_ip6 counter return ip daddr @china_ip 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 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 } ip6 dscp @bypass_dscp counter return meta nfproto @proxy_nfproto jump lan_redirect {% endif %} {% if (fake_ip_ping_hijack): %} ip protocol icmp ip daddr {{ fake_ip_range }} counter redirect {% endif %} } chain mangle_prerouting_lan { type filter hook prerouting priority mangle; policy accept; fib daddr type { local, multicast, broadcast, anycast } counter return ct direction reply counter return ip daddr @reserved_ip counter return ip6 daddr @reserved_ip6 counter return ip daddr @china_ip 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 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 } ip6 dscp @bypass_dscp counter return meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 counter return {% if (tcp_transparent_proxy_mode == 'tproxy'): %} meta nfproto @proxy_nfproto meta l4proto tcp jump lan_tproxy {% elif (tcp_transparent_proxy_mode == 'tun'): %} meta nfproto @proxy_nfproto meta l4proto tcp jump lan_tun {% endif %} {% if (udp_transparent_proxy_mode == 'tproxy'): %} meta nfproto @proxy_nfproto meta l4proto udp jump lan_tproxy {% elif (udp_transparent_proxy_mode == 'tun'): %} meta nfproto @proxy_nfproto meta l4proto udp jump lan_tun {% endif %} } {% endif %} } include "/etc/nikki/nftables/reserved_ip.nft" include "/etc/nikki/nftables/reserved_ip6.nft" {% if (bypass_china_mainland_ip): %} include "/etc/nikki/nftables/geoip_cn.nft" include "/etc/nikki/nftables/geoip6_cn.nft" {% endif %}