op-packages/homeproxy-hiddify/root/etc/homeproxy/scripts/firewall_post.ut
github-actions[bot] ae7b9f4d28 🎁 Sync 2026-07-31 00:21:49
2026-07-31 00:21:49 +08:00

601 lines
22 KiB
Plaintext
Executable File

#!/usr/bin/utpl -S
{%-
import { readfile } from 'fs';
import { cursor } from 'uci';
import { isEmpty } 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.mask} == ${ipv6.addr}`;
}
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)}`;
}
/* 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') || 'proxy_banned_ru';
let outbound_node, outbound_udp_node;
if (routing_mode !== 'custom') {
outbound_node = uci.get(cfgname, 'config', 'main_node') || 'nil';
outbound_udp_node = uci.get(cfgname, 'config', 'main_udp_node') || 'nil';
} else {
outbound_node = uci.get(cfgname, 'routing', 'default_outbound') || 'nil';
}
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';
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;
/* ByeDPI runs as root (needed for TCP_MD5SIG etc.) but under a dedicated group, so its
* egress can be excluded from the redirect by socket group (skgid) to break the proxy
* loop — ciadpi has no fwmark option, and root traffic can't be told apart by skuid. */
const byedpi_enabled = uci.get(cfgname, 'config', 'byedpi_enabled') || '0';
const byedpi_gid = uci.get(cfgname, 'infra', 'byedpi_gid') || '8181';
/* zapret: sing-box's `zapret-out` direct outbound stamps egress with zapret_mark (a
* routing_mark that OVERRIDES default_mark). That mark must (a) escape the redirect loop
* like self_mark does, and (b) be steered into NFQUEUE so nfqws can desync the handshake.
* nfqws reinjects its fake packets carrying its own desync mark (0x40000000) — those must
* also escape the redirect, and must NOT be re-queued. */
const zapret_enabled = uci.get(cfgname, 'config', 'zapret_enabled') || '0';
const zapret_mark = uci.get(cfgname, 'config', 'zapret_mark') || '110';
const zapret_qnum = uci.get(cfgname, 'config', 'zapret_qnum') || '200';
const zapret_desync_mark = '0x40000000'; /* nfqws DESYNC_MARK (its reinjected fakes) */
if (match(proxy_mode, /redirect/)) {
self_mark = uci.get(cfgname, 'infra', 'self_mark') || '100';
redirect_port = uci.get(cfgname, 'infra', 'redirect_port') || '5331';
}
if (match(proxy_mode, /tproxy/))
if (outbound_udp_node !== 'nil' || routing_mode === 'custom' || routing_mode === 'proxy_banned_ru') {
tproxy_port = uci.get(cfgname, 'infra', 'tproxy_port') || '5332';
tproxy_mark = resolve_mark(uci.get(cfgname, 'infra', 'tproxy_mark') || '101');
}
if (match(proxy_mode, /tun/)) {
tun_name = uci.get(cfgname, 'infra', 'tun_name') || '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)
control_info[i] = uci.get(cfgname, 'control', i);
const dns_redirect = uci.get(cfgname, 'infra', 'dns_redirect') || '1';
const dns_hijacked = uci.get('dhcp', '@dnsmasq[0]', 'dns_redirect') || '0',
dns_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 %}
{# 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 :{{ dns_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 */ %}
{% for (let ipv6 in control_info.lan_proxy_ipv6_ips): %}
ip6 saddr {{ resolve_ipv6(ipv6) }} counter goto homeproxy_redirect
{% endfor /* 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 */ %}
{% for (let ipv6 in control_info.lan_direct_ipv6_ips): %}
ip6 saddr {{ resolve_ipv6(ipv6) }} counter return
{% endfor /* 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
{% if (zapret_enabled === '1'): %}
meta mark {{ zapret_mark }} counter return
meta mark and {{ zapret_desync_mark }} != 0 counter return
{% endif /* zapret_enabled */ %}
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 */ %}
{% for (let ipv6 in control_info.lan_global_proxy_ipv6_ips): %}
ip6 saddr {{ resolve_ipv6(ipv6) }} counter goto homeproxy_redirect_proxy_port
{% endfor /* 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 (!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 */ %}
{% for (let ipv6 in control_info.lan_gaming_mode_ipv6_ips): %}
ip6 saddr {{ resolve_ipv6(ipv6) }} counter goto homeproxy_redirect_proxy
{% endfor /* 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
{% if (byedpi_enabled === '1'): %}
meta skgid {{ byedpi_gid }} counter return
{% endif %}
meta nfproto { {{ (ipv6_support === '1') ? 'ipv4, ipv6' : 'ipv4' }} } meta l4proto tcp jump homeproxy_redirect
}
{% endif %}
{# NFQUEUE chain: feed ALL Zapret-marked (mark 110) flows to nfqws2 — any port, TCP
and UDP — and let the strategy's own --filter-tcp/--filter-udp profiles decide what
to desync (the rest passes through via `bypass`). This mirrors how a list→outbound
rule routes all matching traffic: once sing-box marks a flow for zapret-out, the
queue feeds it, regardless of port (so discord.media on 2053/8443, voice UDP, QUIC,
etc. all work with no per-port firewall rules). `ct original packets 1-12` caps it
to each flow's handshake; nfqws's reinjected fakes carry DESYNC_MARK (not mark 110)
so they are never re-queued. #}
{% if (zapret_enabled === '1'): %}
chain homeproxy_zapret_queue {
type filter hook postrouting priority mangle; policy accept;
meta mark {{ zapret_mark }} meta l4proto tcp ct original packets 1-12 counter queue num {{ zapret_qnum }} bypass
meta mark {{ zapret_mark }} meta l4proto udp ct original packets 1-12 counter queue num {{ zapret_qnum }} bypass
}
{% endif %}
{# UDP tproxy #}
{% if (match(proxy_mode, /tproxy/) && (outbound_udp_node !== 'nil' || routing_mode === 'custom' || routing_mode === 'proxy_banned_ru')): %}
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 */ %}
{% for (let ipv6 in control_info.lan_proxy_ipv6_ips): %}
ip6 saddr {{ resolve_ipv6(ipv6) }} counter goto homeproxy_mangle_prerouting
{% endfor /* 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 */ %}
{% for (let ipv6 in control_info.lan_direct_ipv6_ips): %}
ip6 saddr {{ resolve_ipv6(ipv6) }} counter return
{% endfor /* 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 */ %}
{% for (let ipv6 in control_info.lan_global_proxy_ipv6_ips): %}
ip6 saddr {{ resolve_ipv6(ipv6) }} counter goto homeproxy_mangle_tproxy_port
{% endfor /* 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 (!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 */ %}
{% for (let ipv6 in control_info.lan_gaming_mode_ipv6_ips): %}
ip6 saddr {{ resolve_ipv6(ipv6) }} counter goto homeproxy_mangle_tproxy
{% endfor /* 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 {
{% if (byedpi_enabled === '1'): %}
meta skgid {{ byedpi_gid }} counter return
{% endif %}
meta mark {{ self_mark }} counter return
{% if (zapret_enabled === '1'): %}
meta mark {{ zapret_mark }} counter return
meta mark and {{ zapret_desync_mark }} != 0 counter return
{% endif /* zapret_enabled */ %}
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 */ %}
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 */ %}
{% for (let ipv6 in control_info.lan_proxy_ipv6_ips): %}
ip6 saddr {{ resolve_ipv6(ipv6) }} counter goto homeproxy_mangle_tun
{% endfor /* 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 */ %}
{% for (let ipv6 in control_info.lan_direct_ipv6_ips): %}
ip6 saddr {{ resolve_ipv6(ipv6) }} counter return
{% endfor /* 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 */ %}
{% for (let ipv6 in control_info.lan_global_proxy_ipv6_ips): %}
ip6 saddr {{ resolve_ipv6(ipv6) }} counter goto homeproxy_mangle_tun_mark
{% endfor /* 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 (!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 */ %}
{% for (let ipv6 in control_info.lan_gaming_mode_ipv6_ips): %}
ip6 saddr {{ resolve_ipv6(ipv6) }} counter meta mark set {{ tun_mark }}
{% endfor /* 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 %}