mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-29 07:35:13 +08:00
783 lines
28 KiB
Plaintext
Executable File
783 lines
28 KiB
Plaintext
Executable File
#!/usr/bin/utpl -S
|
|
|
|
{%-
|
|
/*
|
|
* Declares every homeproxy_* fw4 chain/set. The inventory of those objects is
|
|
* tracked in scripts/fw4_names.sh, which init.d/homeproxy uses to clean them up
|
|
* on stop; add/remove entries there together with any change below.
|
|
* tests/ucode/test_fw4_names.sh compares both lists, and
|
|
* tests/ucode/test_firewall_template.sh renders this template and checks the
|
|
* objects actually survive into the generated nftables script.
|
|
*
|
|
* Keep this comment inside the ucode block: text placed before `{%-` has its
|
|
* trailing newline trimmed, which would glue it onto the first nft statement.
|
|
*/
|
|
import { readfile } from 'fs';
|
|
import { cursor } from 'uci';
|
|
import { isEmpty, isValidCIDR } from '/etc/homeproxy/scripts/homeproxy.uc';
|
|
|
|
const fw4 = require('fw4');
|
|
|
|
function array_to_nftarr(array) {
|
|
if (type(array) !== 'array')
|
|
return null;
|
|
|
|
return `{ ${join(', ', uniq(array))} }`;
|
|
}
|
|
|
|
function resolve_ipv6(str) {
|
|
if (isEmpty(str))
|
|
return null;
|
|
|
|
let ipv6 = fw4.parse_subnet(str)?.[0];
|
|
if (!ipv6 || ipv6.family !== 6)
|
|
return null;
|
|
|
|
if (ipv6.bits > -1)
|
|
return `${ipv6.addr}/${ipv6.bits}`;
|
|
else
|
|
return ipv6.addr;
|
|
}
|
|
|
|
/* Normalize a list of IPv6 entries into an nftables anonymous set
|
|
(or null when empty), so IPv6 ACLs match in a single set lookup
|
|
instead of one rule per address. */
|
|
function ipv6_to_nftarr(list) {
|
|
if (isEmpty(list))
|
|
return null;
|
|
|
|
let out = [];
|
|
for (let ip in list) {
|
|
const r = resolve_ipv6(ip);
|
|
if (!isEmpty(r))
|
|
push(out, r);
|
|
}
|
|
|
|
return isEmpty(out) ? null : `{ ${join(', ', out)} }`;
|
|
}
|
|
|
|
function resolve_mark(str) {
|
|
if (isEmpty(str))
|
|
return null;
|
|
|
|
let mark = fw4.parse_mark(str);
|
|
if (isEmpty(mark))
|
|
return null;
|
|
|
|
if (mark.mask === 0xffffffff)
|
|
return fw4.hex(mark.mark);
|
|
else if (mark.mark === 0)
|
|
return `mark and ${fw4.hex(~mark.mask & 0xffffffff)}`;
|
|
else if (mark.mark === mark.mask)
|
|
return `mark or ${fw4.hex(mark.mark)}`;
|
|
else if (mark.mask === 0)
|
|
return `mark xor ${fw4.hex(mark.mark)}`;
|
|
else
|
|
return `mark and ${fw4.hex(~mark.mask & 0xffffffff)} xor ${fw4.hex(mark.mark)}`;
|
|
}
|
|
|
|
/*
|
|
* Everything below is interpolated into an nftables script. fw4 loads its
|
|
* includes in one atomic transaction, so a single unparseable value does not
|
|
* just break this rule: every later `fw4 reload` fails and the whole firewall
|
|
* stops being updated. The UI validates its fields, but the values can also
|
|
* come from `uci set`, so each one is checked here as well and dropped when it
|
|
* is not the type the rule expects.
|
|
*/
|
|
const iface_re = /^[A-Za-z0-9._-]{1,15}$/;
|
|
const mac_re = /^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$/;
|
|
|
|
function filter_iface(list, fallback) {
|
|
if (isEmpty(list))
|
|
return list;
|
|
|
|
let out = [];
|
|
for (let i in list)
|
|
if (match(trim(i), iface_re))
|
|
push(out, trim(i));
|
|
|
|
if (isEmpty(out) && !isEmpty(fallback))
|
|
push(out, fallback);
|
|
|
|
return out;
|
|
}
|
|
|
|
function filter_mac(list) {
|
|
if (isEmpty(list))
|
|
return list;
|
|
|
|
let out = [];
|
|
for (let m in list)
|
|
if (match(trim(m), mac_re))
|
|
push(out, trim(m));
|
|
|
|
return out;
|
|
}
|
|
|
|
function filter_ipv4(list) {
|
|
if (isEmpty(list))
|
|
return list;
|
|
|
|
let out = [];
|
|
for (let ip in list)
|
|
if (isValidCIDR(ip, 4))
|
|
push(out, trim(ip));
|
|
|
|
return out;
|
|
}
|
|
|
|
function resolve_port(str, fallback) {
|
|
const port = trim(str ?? '');
|
|
return match(port, /^\d{1,5}$/) && int(port) > 0 && int(port) <= 65535 ? port : fallback;
|
|
}
|
|
|
|
/* Misc config */
|
|
const resources_dir = '/etc/homeproxy/resources';
|
|
|
|
/* UCI config start */
|
|
const cfgname = 'homeproxy';
|
|
const uci = cursor();
|
|
uci.load(cfgname);
|
|
|
|
const routing_mode = uci.get(cfgname, 'config', 'routing_mode') || 'bypass_mainland_china';
|
|
let outbound_node, outbound_udp_node, china_dns_server, bypass_cn_traffic;
|
|
|
|
if (routing_mode !== 'custom') {
|
|
outbound_node = uci.get(cfgname, 'config', 'main_node') || 'nil';
|
|
outbound_udp_node = uci.get(cfgname, 'config', 'main_udp_node') || 'nil';
|
|
china_dns_server = uci.get(cfgname, 'config', 'china_dns_server');
|
|
} else {
|
|
outbound_node = uci.get(cfgname, 'routing', 'default_outbound') || 'nil';
|
|
bypass_cn_traffic = uci.get(cfgname, 'routing', 'bypass_cn_traffic') || '0';
|
|
}
|
|
|
|
let routing_port = uci.get(cfgname, 'config', 'routing_port');
|
|
if (routing_port === 'common')
|
|
routing_port = uci.get(cfgname, 'infra', 'common_port') || '22,53,80,143,443,465,587,853,873,993,995,8080,8443,9418';
|
|
|
|
/* the port list is interpolated into an nftables set: keep only real ports,
|
|
and fall back to "no list" (the guards below then skip the set and its
|
|
rules) when nothing survives */
|
|
if (routing_port) {
|
|
let ports = [];
|
|
for (let port in split(routing_port, ',')) {
|
|
const p = trim(port);
|
|
if (match(p, /^\d{1,5}$/) && int(p) > 0 && int(p) <= 65535)
|
|
push(ports, p);
|
|
}
|
|
routing_port = length(ports) ? join(',', ports) : null;
|
|
}
|
|
|
|
const proxy_mode = uci.get(cfgname, 'config', 'proxy_mode') || 'redirect_tproxy',
|
|
ipv6_support = uci.get(cfgname, 'config', 'ipv6_support') || '0';
|
|
|
|
let self_mark, redirect_port,
|
|
tproxy_port, tproxy_mark,
|
|
tun_name, tun_mark;
|
|
|
|
if (match(proxy_mode, /redirect/)) {
|
|
/* print as a valid mark expression; a hand-edited "value/mask" would
|
|
otherwise reach nft verbatim and break the whole file */
|
|
self_mark = resolve_mark(uci.get(cfgname, 'infra', 'self_mark')) || resolve_mark('100');
|
|
redirect_port = resolve_port(uci.get(cfgname, 'infra', 'redirect_port'), '5331');
|
|
}
|
|
if (match(proxy_mode, /tproxy/))
|
|
if (outbound_udp_node !== 'nil' || routing_mode === 'custom') {
|
|
tproxy_port = resolve_port(uci.get(cfgname, 'infra', 'tproxy_port'), '5332');
|
|
tproxy_mark = resolve_mark(uci.get(cfgname, 'infra', 'tproxy_mark') || '101');
|
|
}
|
|
if (match(proxy_mode, /tun/)) {
|
|
const tun_name_raw = trim(uci.get(cfgname, 'infra', 'tun_name') ?? '');
|
|
tun_name = match(tun_name_raw, iface_re) ? tun_name_raw : 'singtun0';
|
|
tun_mark = resolve_mark(uci.get(cfgname, 'infra', 'tun_mark') || '102');
|
|
}
|
|
|
|
const control_options = [
|
|
"listen_interfaces", "lan_proxy_mode",
|
|
"lan_direct_mac_addrs", "lan_direct_ipv4_ips", "lan_direct_ipv6_ips",
|
|
"lan_proxy_mac_addrs", "lan_proxy_ipv4_ips", "lan_proxy_ipv6_ips",
|
|
"lan_gaming_mode_mac_addrs", "lan_gaming_mode_ipv4_ips", "lan_gaming_mode_ipv6_ips",
|
|
"lan_global_proxy_mac_addrs", "lan_global_proxy_ipv4_ips", "lan_global_proxy_ipv6_ips",
|
|
"wan_proxy_ipv4_ips", "wan_proxy_ipv6_ips",
|
|
"wan_direct_ipv4_ips", "wan_direct_ipv6_ips"
|
|
];
|
|
const control_info = {};
|
|
|
|
for (let i in control_options) {
|
|
const raw = uci.get(cfgname, 'control', i);
|
|
|
|
if (match(i, /_mac_addrs$/))
|
|
control_info[i] = filter_mac(raw);
|
|
else if (match(i, /_ipv4_ips$/))
|
|
control_info[i] = filter_ipv4(raw);
|
|
else if (i === 'listen_interfaces')
|
|
/* fail closed: a configured but unusable interface list must not turn
|
|
into "every interface" (that is exactly what the option restricts) */
|
|
control_info[i] = filter_iface(raw, '__hp_invalid_iface__');
|
|
else
|
|
control_info[i] = raw;
|
|
}
|
|
|
|
const dns_redirect = uci.get(cfgname, 'infra', 'dns_redirect') || '1';
|
|
/* dnsmasq's port, not sing-box's dns_port (the hijack hands the query to
|
|
dnsmasq, which forwards to the sing-box listener itself) */
|
|
const dns_hijacked = uci.get('dhcp', '@dnsmasq[0]', 'dns_redirect') || '0',
|
|
dnsmasq_port = resolve_port(uci.get('dhcp', '@dnsmasq[0]', 'port'), '53');
|
|
/* UCI config end */
|
|
-%}
|
|
|
|
{# Reserved addresses -#}
|
|
set homeproxy_local_addr_v4 {
|
|
type ipv4_addr
|
|
flags interval
|
|
auto-merge
|
|
elements = {
|
|
0.0.0.0/8,
|
|
10.0.0.0/8,
|
|
100.64.0.0/10,
|
|
127.0.0.0/8,
|
|
169.254.0.0/16,
|
|
172.16.0.0/12,
|
|
192.0.0.0/24,
|
|
192.0.2.0/24,
|
|
192.31.196.0/24,
|
|
192.52.193.0/24,
|
|
192.88.99.0/24,
|
|
192.168.0.0/16,
|
|
192.175.48.0/24,
|
|
198.18.0.0/15,
|
|
198.51.100.0/24,
|
|
203.0.113.0/24,
|
|
224.0.0.0/4,
|
|
240.0.0.0/4
|
|
}
|
|
}
|
|
{% if (ipv6_support === '1'): %}
|
|
set homeproxy_local_addr_v6 {
|
|
type ipv6_addr
|
|
flags interval
|
|
auto-merge
|
|
elements = {
|
|
::/128,
|
|
::1/128,
|
|
::ffff:0:0/96,
|
|
100::/64,
|
|
64:ff9b::/96,
|
|
2001::/32,
|
|
2001:10::/28,
|
|
2001:20::/28,
|
|
2001:db8::/28,
|
|
2002::/16,
|
|
fc00::/7,
|
|
fe80::/10,
|
|
ff00::/8
|
|
}
|
|
}
|
|
{% endif %}
|
|
|
|
{% if (routing_mode === 'gfwlist'): %}
|
|
set homeproxy_gfw_list_v4 {
|
|
type ipv4_addr
|
|
flags interval
|
|
auto-merge
|
|
}
|
|
{% if (ipv6_support === '1'): %}
|
|
set homeproxy_gfw_list_v6 {
|
|
type ipv6_addr
|
|
flags interval
|
|
auto-merge
|
|
}
|
|
{% endif /* ipv6_support */ %}
|
|
{% elif (match(routing_mode, /mainland_china/) || bypass_cn_traffic === '1'): %}
|
|
set homeproxy_mainland_addr_v4 {
|
|
type ipv4_addr
|
|
flags interval
|
|
auto-merge
|
|
elements = {
|
|
{% for (let cnip4 in split(trim(readfile(resources_dir + '/china_ip4.txt')), /[\r\n]/)): %}
|
|
{% if (isValidCIDR(cnip4, 4)): %}
|
|
{{ cnip4 }},
|
|
{% endif %}
|
|
{% endfor %}
|
|
}
|
|
}
|
|
{% if ((ipv6_support === '1') || china_dns_server): %}
|
|
set homeproxy_mainland_addr_v6 {
|
|
type ipv6_addr
|
|
flags interval
|
|
auto-merge
|
|
elements = {
|
|
{% for (let cnip6 in split(trim(readfile(resources_dir + '/china_ip6.txt')), /[\r\n]/)): %}
|
|
{% if (isValidCIDR(cnip6, 6)): %}
|
|
{{ cnip6 }},
|
|
{% endif %}
|
|
{% endfor %}
|
|
}
|
|
}
|
|
{% endif /* ipv6_support */ %}
|
|
{% endif /* routing_mode */ %}
|
|
|
|
{# WAN ACL addresses #}
|
|
set homeproxy_wan_proxy_addr_v4 {
|
|
type ipv4_addr
|
|
flags interval
|
|
auto-merge
|
|
{% if (control_info.wan_proxy_ipv4_ips): %}
|
|
elements = { {{ join(', ', control_info.wan_proxy_ipv4_ips) }} }
|
|
{% endif %}
|
|
}
|
|
|
|
{% if (ipv6_support === '1'): %}
|
|
set homeproxy_wan_proxy_addr_v6 {
|
|
type ipv6_addr
|
|
flags interval
|
|
auto-merge
|
|
{% if (control_info.wan_proxy_ipv6_ips): %}
|
|
elements = { {{ join(', ', control_info.wan_proxy_ipv6_ips) }} }
|
|
{% endif /* wan_proxy_ipv6_ips*/ %}
|
|
}
|
|
{% endif /* ipv6_support */ %}
|
|
|
|
set homeproxy_wan_direct_addr_v4 {
|
|
type ipv4_addr
|
|
flags interval
|
|
auto-merge
|
|
{% if (control_info.wan_direct_ipv4_ips): %}
|
|
elements = { {{ join(', ', control_info.wan_direct_ipv4_ips) }} }
|
|
{% endif %}
|
|
}
|
|
|
|
{% if (ipv6_support === '1'): %}
|
|
set homeproxy_wan_direct_addr_v6 {
|
|
type ipv6_addr
|
|
flags interval
|
|
auto-merge
|
|
{% if (control_info.wan_direct_ipv6_ips): %}
|
|
elements = { {{ join(', ', control_info.wan_direct_ipv6_ips) }} }
|
|
{% endif /* wan_direct_ipv6_ips */ %}
|
|
}
|
|
{% endif /* ipv6_support */ %}
|
|
|
|
{% if (routing_port): %}
|
|
set homeproxy_routing_port {
|
|
type inet_service
|
|
flags interval
|
|
auto-merge
|
|
elements = { {{ join(', ', split(routing_port, ',')) }} }
|
|
}
|
|
{% endif %}
|
|
|
|
{# DNS hijack & TCP redirect #}
|
|
chain dstnat {
|
|
{% if ((dns_redirect === '1') && (dns_hijacked !== '1')): %}
|
|
{% if (control_info.listen_interfaces): %}
|
|
meta iifname {{ array_to_nftarr(control_info.listen_interfaces) }}
|
|
{%- endif /* listen_interfaces */ %}
|
|
meta nfproto { ipv4, ipv6 } udp dport 53 counter redirect to :{{ dnsmasq_port }} comment "!{{ cfgname }}: DNS hijack"
|
|
{% endif /* dns_hijacked */ %}
|
|
{% if (match(proxy_mode, /redirect/)): %}
|
|
meta nfproto { {{ (ipv6_support === '1') ? 'ipv4, ipv6' : 'ipv4' }} } meta l4proto tcp jump homeproxy_redirect_lanac
|
|
{% endif /* proxy_mode */ %}
|
|
}
|
|
|
|
{# TCP redirect #}
|
|
{% if (match(proxy_mode, /redirect/)): %}
|
|
chain homeproxy_redirect_proxy {
|
|
meta l4proto tcp counter redirect to :{{ redirect_port }}
|
|
}
|
|
|
|
chain homeproxy_redirect_proxy_port {
|
|
{% if (routing_port): %}
|
|
tcp dport != @homeproxy_routing_port counter return
|
|
{% endif %}
|
|
goto homeproxy_redirect_proxy
|
|
}
|
|
|
|
chain homeproxy_redirect_lanac {
|
|
{% if (control_info.listen_interfaces): %}
|
|
meta iifname != {{ array_to_nftarr(control_info.listen_interfaces) }} counter return
|
|
{% endif %}
|
|
meta mark {{ self_mark }} counter return
|
|
|
|
{% if (control_info.lan_proxy_mode === 'listed_only'): %}
|
|
{% if (!isEmpty(control_info.lan_proxy_ipv4_ips)): %}
|
|
ip saddr {{ array_to_nftarr(control_info.lan_proxy_ipv4_ips) }} counter goto homeproxy_redirect
|
|
{% endif /* lan_proxy_ipv4_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_proxy_ipv6_ips)): %}
|
|
ip6 saddr {{ ipv6_to_nftarr(control_info.lan_proxy_ipv6_ips) }} counter goto homeproxy_redirect
|
|
{% endif /* lan_proxy_ipv6_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_proxy_mac_addrs)): %}
|
|
ether saddr {{ array_to_nftarr(control_info.lan_proxy_mac_addrs) }} counter goto homeproxy_redirect
|
|
{% endif /* lan_proxy_mac_addrs */ %}
|
|
{% elif (control_info.lan_proxy_mode === 'except_listed'): %}
|
|
{% if (!isEmpty(control_info.lan_direct_ipv4_ips)): %}
|
|
ip saddr {{ array_to_nftarr(control_info.lan_direct_ipv4_ips) }} counter return
|
|
{% endif /* lan_direct_ipv4_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_direct_ipv6_ips)): %}
|
|
ip6 saddr {{ ipv6_to_nftarr(control_info.lan_direct_ipv6_ips) }} counter return
|
|
{% endif /* lan_direct_ipv6_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_direct_mac_addrs)): %}
|
|
ether saddr {{ array_to_nftarr(control_info.lan_direct_mac_addrs) }} counter return
|
|
{% endif /* lan_direct_mac_addrs */ %}
|
|
{% endif /* lan_proxy_mode */ %}
|
|
|
|
{% if (control_info.lan_proxy_mode !== 'listed_only'): %}
|
|
counter goto homeproxy_redirect
|
|
{% endif %}
|
|
}
|
|
|
|
chain homeproxy_redirect {
|
|
meta mark {{ self_mark }} counter return
|
|
|
|
ip daddr @homeproxy_wan_proxy_addr_v4 counter goto homeproxy_redirect_proxy_port
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr @homeproxy_wan_proxy_addr_v6 counter goto homeproxy_redirect_proxy_port
|
|
{% endif %}
|
|
|
|
ip daddr @homeproxy_local_addr_v4 counter return
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr @homeproxy_local_addr_v6 counter return
|
|
{% endif %}
|
|
|
|
{% if (routing_mode !== 'custom'): %}
|
|
{% if (!isEmpty(control_info.lan_global_proxy_ipv4_ips)): %}
|
|
ip saddr {{ array_to_nftarr(control_info.lan_global_proxy_ipv4_ips) }} counter goto homeproxy_redirect_proxy_port
|
|
{% endif /* lan_global_proxy_ipv4_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_global_proxy_ipv6_ips)): %}
|
|
ip6 saddr {{ ipv6_to_nftarr(control_info.lan_global_proxy_ipv6_ips) }} counter goto homeproxy_redirect_proxy_port
|
|
{% endif /* lan_global_proxy_ipv6_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_global_proxy_mac_addrs)): %}
|
|
ether saddr {{ array_to_nftarr(control_info.lan_global_proxy_mac_addrs) }} counter goto homeproxy_redirect_proxy_port
|
|
{% endif /* lan_global_proxy_mac_addrs */ %}
|
|
{% endif /* routing_mode */ %}
|
|
|
|
ip daddr @homeproxy_wan_direct_addr_v4 counter return
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr @homeproxy_wan_direct_addr_v6 counter return
|
|
{% endif /* ipv6_support */ %}
|
|
|
|
{% if (routing_mode === 'gfwlist'): %}
|
|
ip daddr != @homeproxy_gfw_list_v4 counter return
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr != @homeproxy_gfw_list_v6 counter return
|
|
{% endif /* ipv6_support */ %}
|
|
{% elif (routing_mode === 'bypass_mainland_china' || bypass_cn_traffic === '1'): %}
|
|
ip daddr @homeproxy_mainland_addr_v4 counter return
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr @homeproxy_mainland_addr_v6 counter return
|
|
{% endif /* ipv6_support */ %}
|
|
{% elif (routing_mode === 'proxy_mainland_china'): %}
|
|
ip daddr != @homeproxy_mainland_addr_v4 counter return
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr != @homeproxy_mainland_addr_v6 counter return
|
|
{% endif /* ipv6_support */ %}
|
|
{% endif /* routing_mode */ %}
|
|
|
|
{% if (!isEmpty(control_info.lan_gaming_mode_ipv4_ips)): %}
|
|
ip saddr {{ array_to_nftarr(control_info.lan_gaming_mode_ipv4_ips) }} counter goto homeproxy_redirect_proxy
|
|
{% endif /* lan_gaming_mode_ipv4_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_gaming_mode_ipv6_ips)): %}
|
|
ip6 saddr {{ ipv6_to_nftarr(control_info.lan_gaming_mode_ipv6_ips) }} counter goto homeproxy_redirect_proxy
|
|
{% endif /* lan_gaming_mode_ipv6_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_gaming_mode_mac_addrs)): %}
|
|
ether saddr {{ array_to_nftarr(control_info.lan_gaming_mode_mac_addrs) }} counter goto homeproxy_redirect_proxy
|
|
{% endif /* lan_gaming_mode_mac_addrs */ %}
|
|
|
|
counter goto homeproxy_redirect_proxy_port
|
|
}
|
|
|
|
chain homeproxy_output_redir {
|
|
type nat hook output priority filter -105; policy accept
|
|
meta nfproto { {{ (ipv6_support === '1') ? 'ipv4, ipv6' : 'ipv4' }} } meta l4proto tcp jump homeproxy_redirect
|
|
}
|
|
{% endif %}
|
|
|
|
{# UDP tproxy #}
|
|
{% if (match(proxy_mode, /tproxy/) && (outbound_udp_node !== 'nil' || routing_mode === 'custom')): %}
|
|
chain homeproxy_mangle_tproxy {
|
|
meta l4proto udp meta mark set {{ tproxy_mark }} tproxy ip to 127.0.0.1:{{ tproxy_port }} counter accept
|
|
{% if (ipv6_support === '1'): %}
|
|
meta l4proto udp meta mark set {{ tproxy_mark }} tproxy ip6 to [::1]:{{ tproxy_port }} counter accept
|
|
{% endif %}
|
|
}
|
|
|
|
chain homeproxy_mangle_tproxy_port {
|
|
{% if (routing_port): %}
|
|
udp dport != @homeproxy_routing_port counter return
|
|
{% endif %}
|
|
goto homeproxy_mangle_tproxy
|
|
}
|
|
|
|
chain homeproxy_mangle_mark {
|
|
{% if (routing_port): %}
|
|
udp dport != @homeproxy_routing_port counter return
|
|
{% endif %}
|
|
meta l4proto udp meta mark set {{ tproxy_mark }} counter accept
|
|
}
|
|
|
|
chain homeproxy_mangle_lanac {
|
|
{% if (control_info.listen_interfaces): %}
|
|
meta iifname != {{ array_to_nftarr(uniq([...control_info.listen_interfaces, ...['lo']])) }} counter return
|
|
{% endif %}
|
|
meta iifname != lo udp dport 53 counter return
|
|
meta mark {{ self_mark }} counter return
|
|
|
|
{% if (control_info.lan_proxy_mode === 'listed_only'): %}
|
|
{% if (!isEmpty(control_info.lan_proxy_ipv4_ips)): %}
|
|
ip saddr {{ array_to_nftarr(control_info.lan_proxy_ipv4_ips) }} counter goto homeproxy_mangle_prerouting
|
|
{% endif /* lan_proxy_ipv4_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_proxy_ipv6_ips)): %}
|
|
ip6 saddr {{ ipv6_to_nftarr(control_info.lan_proxy_ipv6_ips) }} counter goto homeproxy_mangle_prerouting
|
|
{% endif /* lan_proxy_ipv6_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_proxy_mac_addrs)): %}
|
|
ether saddr {{ array_to_nftarr(control_info.lan_proxy_mac_addrs) }} counter goto homeproxy_mangle_prerouting
|
|
{% endif /* lan_proxy_mac_addrs */ %}
|
|
{% elif (control_info.lan_proxy_mode === 'except_listed'): %}
|
|
{% if (!isEmpty(control_info.lan_direct_ipv4_ips)): %}
|
|
ip saddr {{ array_to_nftarr(control_info.lan_direct_ipv4_ips) }} counter return
|
|
{% endif /* lan_direct_ipv4_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_direct_ipv6_ips)): %}
|
|
ip6 saddr {{ ipv6_to_nftarr(control_info.lan_direct_ipv6_ips) }} counter return
|
|
{% endif /* lan_direct_ipv6_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_direct_mac_addrs)): %}
|
|
ether saddr {{ array_to_nftarr(control_info.lan_direct_mac_addrs) }} counter return
|
|
{% endif /* lan_direct_mac_addrs */ %}
|
|
{% endif /* lan_proxy_mode */ %}
|
|
|
|
{% if (control_info.lan_proxy_mode !== 'listed_only'): %}
|
|
counter goto homeproxy_mangle_prerouting
|
|
{% endif %}
|
|
}
|
|
|
|
chain homeproxy_mangle_prerouting {
|
|
ip daddr @homeproxy_wan_proxy_addr_v4 counter goto homeproxy_mangle_tproxy_port
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr @homeproxy_wan_proxy_addr_v6 counter goto homeproxy_mangle_tproxy_port
|
|
{% endif %}
|
|
|
|
ip daddr @homeproxy_local_addr_v4 counter return
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr @homeproxy_local_addr_v6 counter return
|
|
{% endif %}
|
|
|
|
{% if (routing_mode !== 'custom'): %}
|
|
{% if (!isEmpty(control_info.lan_global_proxy_ipv4_ips)): %}
|
|
ip saddr {{ array_to_nftarr(control_info.lan_global_proxy_ipv4_ips) }} counter goto homeproxy_mangle_tproxy_port
|
|
{% endif /* lan_global_proxy_ipv4_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_global_proxy_ipv6_ips)): %}
|
|
ip6 saddr {{ ipv6_to_nftarr(control_info.lan_global_proxy_ipv6_ips) }} counter goto homeproxy_mangle_tproxy_port
|
|
{% endif /* lan_global_proxy_ipv6_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_global_proxy_mac_addrs)): %}
|
|
ether saddr {{ array_to_nftarr(control_info.lan_global_proxy_mac_addrs) }} counter goto homeproxy_mangle_tproxy_port
|
|
{% endif /* lan_global_proxy_mac_addrs */ %}
|
|
{% endif /* routing_mode */ %}
|
|
|
|
ip daddr @homeproxy_wan_direct_addr_v4 counter return
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr @homeproxy_wan_direct_addr_v6 counter return
|
|
{% endif /* ipv6_support */ %}
|
|
|
|
{% if (routing_mode === 'gfwlist'): %}
|
|
ip daddr != @homeproxy_gfw_list_v4 counter return
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr != @homeproxy_gfw_list_v6 counter return
|
|
{% endif /* ipv6_support */ %}
|
|
udp dport { 80, 443 } counter reject comment "!{{ cfgname }}: Block QUIC (not proxyable)"
|
|
{% elif (routing_mode === 'bypass_mainland_china' || bypass_cn_traffic === '1'): %}
|
|
ip daddr @homeproxy_mainland_addr_v4 counter return
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr @homeproxy_mainland_addr_v6 counter return
|
|
{% endif /* ipv6_support */ %}
|
|
{% if (routing_mode !== 'custom'): %}
|
|
udp dport { 80, 443 } counter reject comment "!{{ cfgname }}: Block QUIC (not proxyable)"
|
|
{% endif /* routing_mode */ %}
|
|
{% elif (routing_mode === 'proxy_mainland_china'): %}
|
|
ip daddr != @homeproxy_mainland_addr_v4 counter return
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr != @homeproxy_mainland_addr_v6 counter return
|
|
{% endif /* ipv6_support */ %}
|
|
{% endif /* routing_mode */ %}
|
|
|
|
{% if (!isEmpty(control_info.lan_gaming_mode_ipv4_ips)): %}
|
|
ip saddr {{ array_to_nftarr(control_info.lan_gaming_mode_ipv4_ips) }} counter goto homeproxy_mangle_tproxy
|
|
{% endif /* lan_gaming_mode_ipv4_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_gaming_mode_ipv6_ips)): %}
|
|
ip6 saddr {{ ipv6_to_nftarr(control_info.lan_gaming_mode_ipv6_ips) }} counter goto homeproxy_mangle_tproxy
|
|
{% endif /* lan_gaming_mode_ipv6_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_gaming_mode_mac_addrs)): %}
|
|
ether saddr {{ array_to_nftarr(control_info.lan_gaming_mode_mac_addrs) }} counter goto homeproxy_mangle_tproxy
|
|
{% endif /* lan_gaming_mode_mac_addrs */ %}
|
|
|
|
counter goto homeproxy_mangle_tproxy_port
|
|
}
|
|
|
|
chain homeproxy_mangle_output {
|
|
meta mark {{ self_mark }} counter return
|
|
|
|
ip daddr @homeproxy_wan_proxy_addr_v4 counter goto homeproxy_mangle_mark
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr @homeproxy_wan_proxy_addr_v6 counter goto homeproxy_mangle_mark
|
|
{% endif %}
|
|
|
|
ip daddr @homeproxy_local_addr_v4 counter return
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr @homeproxy_local_addr_v6 counter return
|
|
{% endif %}
|
|
|
|
ip daddr @homeproxy_wan_direct_addr_v4 counter return
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr @homeproxy_wan_direct_addr_v6 counter return
|
|
{% endif /* ipv6_support */ %}
|
|
|
|
{% if (routing_mode === 'gfwlist'): %}
|
|
ip daddr != @homeproxy_gfw_list_v4 counter return
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr != @homeproxy_gfw_list_v6 counter return
|
|
{% endif /* ipv6_support */ %}
|
|
{% elif (routing_mode === 'bypass_mainland_china' || bypass_cn_traffic === '1'): %}
|
|
ip daddr @homeproxy_mainland_addr_v4 counter return
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr @homeproxy_mainland_addr_v6 counter return
|
|
{% endif /* ipv6_support */ %}
|
|
{% elif (routing_mode === 'proxy_mainland_china'): %}
|
|
ip daddr != @homeproxy_mainland_addr_v4 counter return
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr != @homeproxy_mainland_addr_v6 counter return
|
|
{% endif /* ipv6_support */ %}
|
|
{% endif /* routing_mode */ %}
|
|
|
|
counter goto homeproxy_mangle_mark
|
|
}
|
|
|
|
chain mangle_prerouting {
|
|
meta nfproto { {{ (ipv6_support === '1') ? 'ipv4, ipv6' : 'ipv4' }} } meta l4proto udp jump homeproxy_mangle_lanac
|
|
}
|
|
|
|
chain mangle_output {
|
|
meta nfproto { {{ (ipv6_support === '1') ? 'ipv4, ipv6' : 'ipv4' }} } meta l4proto udp jump homeproxy_mangle_output
|
|
}
|
|
{% endif %}
|
|
|
|
{# TUN #}
|
|
{% if (match(proxy_mode, /tun/)): %}
|
|
chain homeproxy_mangle_lanac {
|
|
iifname {{ tun_name }} counter return
|
|
udp dport 53 counter return
|
|
|
|
{% if (control_info.listen_interfaces): %}
|
|
meta iifname != {{ array_to_nftarr(control_info.listen_interfaces) }} counter return
|
|
{% endif %}
|
|
|
|
{% if (control_info.lan_proxy_mode === 'listed_only'): %}
|
|
{% if (!isEmpty(control_info.lan_proxy_ipv4_ips)): %}
|
|
ip saddr {{ array_to_nftarr(control_info.lan_proxy_ipv4_ips) }} counter goto homeproxy_mangle_tun
|
|
{% endif /* lan_proxy_ipv4_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_proxy_ipv6_ips)): %}
|
|
ip6 saddr {{ ipv6_to_nftarr(control_info.lan_proxy_ipv6_ips) }} counter goto homeproxy_mangle_tun
|
|
{% endif /* lan_proxy_ipv6_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_proxy_mac_addrs)): %}
|
|
ether saddr {{ array_to_nftarr(control_info.lan_proxy_mac_addrs) }} counter goto homeproxy_mangle_tun
|
|
{% endif /* lan_proxy_mac_addrs */ %}
|
|
{% elif (control_info.lan_proxy_mode === 'except_listed'): %}
|
|
{% if (!isEmpty(control_info.lan_direct_ipv4_ips)): %}
|
|
ip saddr {{ array_to_nftarr(control_info.lan_direct_ipv4_ips) }} counter return
|
|
{% endif /* lan_direct_ipv4_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_direct_ipv6_ips)): %}
|
|
ip6 saddr {{ ipv6_to_nftarr(control_info.lan_direct_ipv6_ips) }} counter return
|
|
{% endif /* lan_direct_ipv6_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_direct_mac_addrs)): %}
|
|
ether saddr {{ array_to_nftarr(control_info.lan_direct_mac_addrs) }} counter return
|
|
{% endif /* lan_direct_mac_addrs */ %}
|
|
{% endif /* lan_proxy_mode */ %}
|
|
|
|
{% if (control_info.lan_proxy_mode !== 'listed_only'): %}
|
|
counter goto homeproxy_mangle_tun
|
|
{% endif %}
|
|
}
|
|
|
|
chain homeproxy_mangle_tun_mark {
|
|
{% if (routing_port): %}
|
|
{% if (proxy_mode === 'tun'): %}
|
|
tcp dport != @homeproxy_routing_port counter return
|
|
{% endif /* proxy_mode */ %}
|
|
udp dport != @homeproxy_routing_port counter return
|
|
{% endif /* routing_port */ %}
|
|
|
|
counter meta mark set {{ tun_mark }}
|
|
}
|
|
|
|
chain homeproxy_mangle_tun {
|
|
iifname {{ tun_name }} counter return
|
|
|
|
ip daddr @homeproxy_wan_proxy_addr_v4 counter goto homeproxy_mangle_tun_mark
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr @homeproxy_wan_proxy_addr_v6 counter goto homeproxy_mangle_tun_mark
|
|
{% endif %}
|
|
|
|
ip daddr @homeproxy_local_addr_v4 counter return
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr @homeproxy_local_addr_v6 counter return
|
|
{% endif %}
|
|
|
|
{% if (routing_mode !== 'custom'): %}
|
|
{% if (!isEmpty(control_info.lan_global_proxy_ipv4_ips)): %}
|
|
ip saddr {{ array_to_nftarr(control_info.lan_global_proxy_ipv4_ips) }} counter goto homeproxy_mangle_tun_mark
|
|
{% endif /* lan_global_proxy_ipv4_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_global_proxy_ipv6_ips)): %}
|
|
ip6 saddr {{ ipv6_to_nftarr(control_info.lan_global_proxy_ipv6_ips) }} counter goto homeproxy_mangle_tun_mark
|
|
{% endif /* lan_global_proxy_ipv6_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_global_proxy_mac_addrs)): %}
|
|
ether saddr {{ array_to_nftarr(control_info.lan_global_proxy_mac_addrs) }} counter goto homeproxy_mangle_tun_mark
|
|
{% endif /* lan_global_proxy_mac_addrs */ %}
|
|
{% endif /* routing_mode */ %}
|
|
|
|
{% if (control_info.wan_direct_ipv4_ips): %}
|
|
ip daddr {{ array_to_nftarr(control_info.wan_direct_ipv4_ips) }} counter return
|
|
{% endif /* wan_direct_ipv4_ips */ %}
|
|
{% if (control_info.wan_direct_ipv6_ips): %}
|
|
ip6 daddr {{ array_to_nftarr(control_info.wan_direct_ipv6_ips) }} counter return
|
|
{% endif /* wan_direct_ipv6_ips */ %}
|
|
|
|
{% if (routing_mode === 'gfwlist'): %}
|
|
ip daddr != @homeproxy_gfw_list_v4 counter return
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr != @homeproxy_gfw_list_v6 counter return
|
|
{% endif /* ipv6_support */ %}
|
|
udp dport { 80, 443 } counter reject comment "!{{ cfgname }}: Block QUIC (not proxyable)"
|
|
{% elif (routing_mode === 'bypass_mainland_china' || bypass_cn_traffic === '1'): %}
|
|
ip daddr @homeproxy_mainland_addr_v4 counter return
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr @homeproxy_mainland_addr_v6 counter return
|
|
{% endif /* ipv6_support */ %}
|
|
{% if (routing_mode !== 'custom'): %}
|
|
udp dport { 80, 443 } counter reject comment "!{{ cfgname }}: Block QUIC (not proxyable)"
|
|
{% endif /* routing_mode */ %}
|
|
{% elif (routing_mode === 'proxy_mainland_china'): %}
|
|
ip daddr != @homeproxy_mainland_addr_v4 counter return
|
|
{% if (ipv6_support === '1'): %}
|
|
ip6 daddr != @homeproxy_mainland_addr_v6 counter return
|
|
{% endif /* ipv6_support */ %}
|
|
{% endif /* routing_mode */ %}
|
|
|
|
{% if (!isEmpty(control_info.lan_gaming_mode_ipv4_ips)): %}
|
|
ip saddr {{ array_to_nftarr(control_info.lan_gaming_mode_ipv4_ips) }} counter meta mark set {{ tun_mark }}
|
|
{% endif /* lan_gaming_mode_ipv4_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_gaming_mode_ipv6_ips)): %}
|
|
ip6 saddr {{ ipv6_to_nftarr(control_info.lan_gaming_mode_ipv6_ips) }} counter meta mark set {{ tun_mark }}
|
|
{% endif /* lan_gaming_mode_ipv6_ips */ %}
|
|
{% if (!isEmpty(control_info.lan_gaming_mode_mac_addrs)): %}
|
|
ether saddr {{ array_to_nftarr(control_info.lan_gaming_mode_mac_addrs) }} counter meta mark set {{ tun_mark }}
|
|
{% endif /* lan_gaming_mode_mac_addrs */ %}
|
|
|
|
counter goto homeproxy_mangle_tun_mark
|
|
}
|
|
|
|
chain mangle_prerouting {
|
|
meta nfproto { {{ (ipv6_support === '1') ? 'ipv4, ipv6' : 'ipv4' }} } meta l4proto { {{ (proxy_mode === 'tun') ? 'tcp, udp' : 'udp' }} } jump homeproxy_mangle_lanac
|
|
}
|
|
|
|
chain mangle_output {
|
|
meta nfproto { {{ (ipv6_support === '1') ? 'ipv4, ipv6' : 'ipv4' }} } meta l4proto { {{ (proxy_mode === 'tun') ? 'tcp, udp' : 'udp' }} } jump homeproxy_mangle_tun
|
|
}
|
|
{% endif %}
|