#!/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 } from '/etc/nikki/ucode/include.uc'; const cgroups_version = get_cgroups_version(); const users = get_users(); const groups = get_groups(); const cgroups = get_cgroups(); 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_listen = uci.get('nikki', 'mixin', 'dns_listen'); const dns_port = substr(dns_listen, rindex(dns_listen, ':') + 1); const fake_ip_range = uci.get('nikki', 'mixin', 'fake_ip_range'); const tun_device = uci.get('nikki', 'mixin', 'tun_device'); 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']); 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 device = ubus.call('network.interface', 'status', {'interface': interface})?.l3_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']); push(lan_access_control, access_control); }); 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 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 = { {% for (let nfproto in dns_hijack_nfproto): %} {{ nfproto }}, {% endfor %} } {% endif %} } set proxy_nfproto { type nf_proto flags interval {% if (length(proxy_nfproto) > 0): %} elements = { {% for (let nfproto in proxy_nfproto): %} {{ nfproto }}, {% endfor %} } {% endif %} } set reserved_ip { type ipv4_addr flags interval auto-merge } set reserved_ip6 { type ipv6_addr flags interval auto-merge } set lan_inbound_device { type ifname flags interval auto-merge {% if (length(lan_inbound_device) > 0): %} elements = { {% for (let device in lan_inbound_device): %} {{ device }}, {% endfor %} } {% 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 = { {% for (let dport in proxy_dport): %} {{ dport }}, {% endfor %} } {% endif %} } set bypass_dscp { type dscp flags interval auto-merge {% if (length(bypass_dscp) > 0): %} elements = { {% for (let dscp in bypass_dscp): %} {{ dscp }}, {% endfor %} } {% endif %} } chain router_dns_hijack { {% for (let access_control in router_access_control): %} {% if (access_control['enabled']): %} {% if (length(access_control['user']) == 0 && length(access_control['group']) == 0 && length(access_control['cgroup']) == 0): %} meta l4proto { tcp, udp } th dport 53 counter {% if (access_control.proxy == '1'): %} redirect to :{{ dns_port }} {% else %} return {% endif %} {% else %} {% if (length(access_control['user']) > 0): %} meta l4proto { tcp, udp } meta skuid { {% for (let user in access_control['user']): %} {{ user }}, {% endfor %} } th dport 53 counter {% if (access_control.proxy == '1'): %} redirect to :{{ dns_port }} {% else %} return {% endif %} {% endif %} {% if (length(access_control['group']) > 0): %} meta l4proto { tcp, udp } meta skgid { {% for (let group in access_control['group']): %} {{ group }}, {% endfor %} } th dport 53 counter {% if (access_control.proxy == '1'): %} redirect to :{{ dns_port }} {% else %} return {% endif %} {% endif %} {% if (cgroups_version == 2 && length(access_control['cgroup']) > 0): %} meta l4proto { tcp, udp } socket cgroupv2 level 2 { {% for (let cgroup in access_control['cgroup']): %} services/{{ cgroup }}, {% endfor %} } th dport 53 counter {% if (access_control.proxy == '1'): %} redirect to :{{ dns_port }} {% else %} return {% endif %} {% endif %} {% endif %} {% endif %} {% endfor %} } chain router_redirect { {% for (let access_control in router_access_control): %} {% if (access_control['enabled']): %} {% if (length(access_control['user']) == 0 && length(access_control['group']) == 0 && length(access_control['cgroup']) == 0): %} meta l4proto tcp counter {% if (access_control.proxy == '1'): %} redirect to :{{ redir_port }} {% else %} return {% endif %} {% else %} {% if (length(access_control['user']) > 0): %} meta l4proto tcp meta skuid { {% for (let user in access_control['user']): %} {{ user }}, {% endfor %} } counter {% if (access_control.proxy == '1'): %} redirect to :{{ redir_port }} {% else %} return {% endif %} {% endif %} {% if (length(access_control['group']) > 0): %} meta l4proto tcp meta skgid { {% for (let group in access_control['group']): %} {{ group }}, {% endfor %} } counter {% if (access_control.proxy == '1'): %} redirect to :{{ redir_port }} {% else %} return {% endif %} {% endif %} {% if (cgroups_version == 2 && length(access_control['cgroup']) > 0): %} meta l4proto tcp socket cgroupv2 level 2 { {% for (let cgroup in access_control['cgroup']): %} services/{{ cgroup }}, {% endfor %} } counter {% if (access_control.proxy == '1'): %} redirect to :{{ redir_port }} {% else %} return {% endif %} {% endif %} {% endif %} {% endif %} {% endfor %} } chain router_tproxy { {% for (let access_control in router_access_control): %} {% if (access_control['enabled']): %} {% if (length(access_control['user']) == 0 && length(access_control['group']) == 0 && length(access_control['cgroup']) == 0): %} meta l4proto { tcp, udp } {% if (access_control.proxy == '1'): %} meta mark set {{ tproxy_fw_mark }} counter accept {% else %} counter return {% endif %} {% else %} {% if (length(access_control['user']) > 0): %} meta l4proto { tcp, udp } meta skuid { {% for (let user in access_control['user']): %} {{ user }}, {% endfor %} } {% if (access_control.proxy == '1'): %} meta mark set {{ tproxy_fw_mark }} counter accept {% else %} counter return {% endif %} {% endif %} {% if (length(access_control['group']) > 0): %} meta l4proto { tcp, udp } meta skgid { {% for (let group in access_control['group']): %} {{ group }}, {% endfor %} } {% if (access_control.proxy == '1'): %} meta mark set {{ tproxy_fw_mark }} counter accept {% else %} counter return {% endif %} {% endif %} {% if (cgroups_version == 2 && length(access_control['cgroup']) > 0): %} meta l4proto { tcp, udp } socket cgroupv2 level 2 { {% for (let cgroup in access_control['cgroup']): %} services/{{ cgroup }}, {% endfor %} } {% if (access_control.proxy == '1'): %} meta mark set {{ tproxy_fw_mark }} counter accept {% else %} counter return {% endif %} {% endif %} {% endif %} {% endif %} {% endfor %} } chain router_tun { {% for (let access_control in router_access_control): %} {% if (access_control['enabled']): %} {% if (length(access_control['user']) == 0 && length(access_control['group']) == 0 && length(access_control['cgroup']) == 0): %} meta l4proto { tcp, udp } {% if (access_control.proxy == '1'): %} meta mark set {{ tun_fw_mark }} counter accept {% else %} counter return {% endif %} {% else %} {% if (length(access_control['user']) > 0): %} meta l4proto { tcp, udp } meta skuid { {% for (let user in access_control['user']): %} {{ user }}, {% endfor %} } {% if (access_control.proxy == '1'): %} meta mark set {{ tun_fw_mark }} counter accept {% else %} counter return {% endif %} {% endif %} {% if (length(access_control['group']) > 0): %} meta l4proto { tcp, udp } meta skgid { {% for (let group in access_control['group']): %} {{ group }}, {% endfor %} } {% if (access_control.proxy == '1'): %} meta mark set {{ tun_fw_mark }} counter accept {% else %} counter return {% endif %} {% endif %} {% if (cgroups_version == 2 && length(access_control['cgroup']) > 0): %} meta l4proto { tcp, udp } socket cgroupv2 level 2 { {% for (let cgroup in access_control['cgroup']): %} services/{{ cgroup }}, {% endfor %} } {% if (access_control.proxy == '1'): %} meta mark set {{ tun_fw_mark }} counter accept {% else %} counter return {% endif %} {% endif %} {% endif %} {% endif %} {% endfor %} } chain lan_dns_hijack { {% for (let access_control in lan_access_control): %} {% if (access_control['enabled']): %} {% if (length(access_control['ip']) == 0 && length(access_control['ip6']) == 0 && length(access_control['mac']) == 0): %} meta l4proto { tcp, udp } th dport 53 counter {% if (access_control.proxy == '1'): %} redirect to :{{ dns_port }} {% else %} return {% endif %} {% else %} {% if (length(access_control['ip']) > 0): %} meta l4proto { tcp, udp } ip saddr { {% for (let ip in access_control['ip']): %} {{ ip }}, {% endfor %} } th dport 53 counter {% if (access_control.proxy == '1'): %} redirect to :{{ dns_port }} {% else %} return {% endif %} {% endif %} {% if (length(access_control['ip6']) > 0): %} meta l4proto { tcp, udp } ip6 saddr { {% for (let ip6 in access_control['ip6']): %} {{ ip6 }}, {% endfor %} } th dport 53 counter {% if (access_control.proxy == '1'): %} redirect to :{{ dns_port }} {% else %} return {% endif %} {% endif %} {% if (length(access_control['mac']) > 0): %} meta l4proto { tcp, udp } ether saddr { {% for (let mac in access_control['mac']): %} {{ mac }}, {% endfor %} } th dport 53 counter {% if (access_control.proxy == '1'): %} redirect to :{{ dns_port }} {% else %} return {% endif %} {% endif %} {% endif %} {% endif %} {% endfor %} } chain lan_redirect { {% for (let access_control in lan_access_control): %} {% if (access_control['enabled']): %} {% if (length(access_control['ip']) == 0 && length(access_control['ip6']) == 0 && length(access_control['mac']) == 0): %} meta l4proto tcp counter {% if (access_control.proxy == '1'): %} redirect to :{{ redir_port }} {% else %} counter return {% endif %} {% else %} {% if (length(access_control['ip']) > 0): %} meta l4proto tcp ip saddr { {% for (let ip in access_control['ip']): %} {{ ip }}, {% endfor %} } counter {% if (access_control.proxy == '1'): %} redirect to :{{ redir_port }} {% else %} return {% endif %} {% endif %} {% if (length(access_control['ip6']) > 0): %} meta l4proto tcp ip6 saddr { {% for (let ip6 in access_control['ip6']): %} {{ ip6 }}, {% endfor %} } counter {% if (access_control.proxy == '1'): %} redirect to :{{ redir_port }} {% else %} return {% endif %} {% endif %} {% if (length(access_control['mac']) > 0): %} meta l4proto tcp ether saddr { {% for (let mac in access_control['mac']): %} {{ mac }}, {% endfor %} } counter {% if (access_control.proxy == '1'): %} redirect to :{{ redir_port }} {% else %} return {% endif %} {% endif %} {% endif %} {% endif %} {% endfor %} } chain lan_tproxy { {% for (let access_control in lan_access_control): %} {% if (access_control['enabled']): %} {% if (length(access_control['ip']) == 0 && length(access_control['ip6']) == 0 && length(access_control['mac']) == 0): %} meta l4proto { tcp, udp } {% if (access_control.proxy == '1'): %} meta mark set {{ tproxy_fw_mark }} tproxy to :{{ tproxy_port }} counter accept {% else %} counter return {% endif %} {% else %} {% if (length(access_control['ip']) > 0): %} meta l4proto { tcp, udp } ip saddr { {% for (let ip in access_control['ip']): %} {{ ip }}, {% endfor %} } {% if (access_control.proxy == '1'): %} meta mark set {{ tproxy_fw_mark }} tproxy ip to :{{ tproxy_port }} counter accept {% else %} counter return {% endif %} {% endif %} {% if (length(access_control['ip6']) > 0): %} meta l4proto { tcp, udp } ip6 saddr { {% for (let ip6 in access_control['ip6']): %} {{ ip6 }}, {% endfor %} } {% if (access_control.proxy == '1'): %} meta mark set {{ tproxy_fw_mark }} tproxy ip6 to :{{ tproxy_port }} counter accept {% else %} counter return {% endif %} {% endif %} {% if (length(access_control['mac']) > 0): %} meta l4proto { tcp, udp } ether saddr { {% for (let mac in access_control['mac']): %} {{ mac }}, {% endfor %} } {% if (access_control.proxy == '1'): %} meta mark set {{ tproxy_fw_mark }} tproxy to :{{ tproxy_port }} counter accept {% else %} counter return {% endif %} {% endif %} {% endif %} {% endif %} {% endfor %} } chain lan_tun { {% for (let access_control in lan_access_control): %} {% if (access_control['enabled']): %} {% if (length(access_control['ip']) == 0 && length(access_control['ip6']) == 0 && length(access_control['mac']) == 0): %} meta l4proto { tcp, udp } {% if (access_control.proxy == '1'): %} meta mark set {{ tun_fw_mark }} counter accept {% else %}counter return {% endif %} {% else %} {% if (length(access_control['ip']) > 0): %} meta l4proto { tcp, udp } ip saddr { {% for (let ip in access_control['ip']): %} {{ ip }}, {% endfor %} } {% if (access_control.proxy == '1'): %} meta mark set {{ tun_fw_mark }} counter accept {% else %}counter return {% endif %} {% endif %} {% if (length(access_control['ip6']) > 0): %} meta l4proto { tcp, udp } ip6 saddr { {% for (let ip6 in access_control['ip6']): %} {{ ip6 }}, {% endfor %} } {% if (access_control.proxy == '1'): %} meta mark set {{ tun_fw_mark }} counter accept {% else %}counter return {% endif %} {% endif %} {% if (length(access_control['mac']) > 0): %} meta l4proto { tcp, udp } ether saddr { {% for (let mac in access_control['mac']): %} {{ mac }}, {% endfor %} } {% if (access_control.proxy == '1'): %} meta mark set {{ tun_fw_mark }} counter accept {% else %}counter return {% endif %} {% endif %} {% endif %} {% endif %} {% endfor %} } {% 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 %} meta nfproto @dns_hijack_nfproto jump router_dns_hijack {% if (tcp_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 router_redirect {% endif %} {% if (fake_ip_ping_hijack): %} ip protocol icmp icmp type echo-request ip daddr {{ fake_ip_range }} counter redirect {% 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, 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_mode == 'tproxy'): %} meta nfproto @proxy_nfproto meta l4proto tcp jump router_tproxy {% elif (tcp_mode == 'tun'): %} meta nfproto @proxy_nfproto meta l4proto tcp jump router_tun {% endif %} {% if (udp_mode == 'tproxy'): %} meta nfproto @proxy_nfproto meta l4proto udp jump router_tproxy {% elif (udp_mode == 'tun'): %} meta nfproto @proxy_nfproto meta l4proto udp jump router_tun {% 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_mark }} tproxy to :{{ tproxy_port }} counter accept {% endif %} {% if (tcp_mode == 'tun' || udp_mode == 'tun'): %} iifname {{ tun_device }} meta l4proto { icmp, tcp, udp } counter accept {% endif %} } {% endif %} {% if (lan_proxy): %} chain dstnat { type nat hook prerouting priority dstnat + 1; policy accept; iifname @lan_inbound_device meta nfproto @dns_hijack_nfproto jump lan_dns_hijack {% if (tcp_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 iifname @lan_inbound_device meta nfproto @proxy_nfproto jump lan_redirect {% endif %} {% if (fake_ip_ping_hijack): %} ip protocol icmp icmp type echo-request 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_mode == 'tproxy'): %} iifname @lan_inbound_device meta nfproto @proxy_nfproto meta l4proto tcp jump lan_tproxy {% elif (tcp_mode == 'tun'): %} iifname @lan_inbound_device meta nfproto @proxy_nfproto meta l4proto tcp jump lan_tun {% endif %} {% if (udp_mode == 'tproxy'): %} iifname @lan_inbound_device meta nfproto @proxy_nfproto meta l4proto udp jump lan_tproxy {% elif (udp_mode == 'tun'): %} iifname @lan_inbound_device 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 %}