mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-04 03:43:56 +08:00
🦄 Sync 2026-10-02 14:52:05
This commit is contained in:
1 parent
4c85793342
commit
0bfe84f1e8
133 files changed
+7983
-3225
No files matched your search
@@ -165,6 +165,58 @@ if (routing_port === 'common')
|
||||
const proxy_mode = uci.get(cfgname, 'config', 'proxy_mode') || 'redirect_tproxy',
|
||||
ipv6_support = uci.get(cfgname, 'config', 'ipv6_support') || '0';
|
||||
|
||||
/* --- can IPv6 be classified as mainland at all? ------------------------
|
||||
*
|
||||
* Every IPv6 decision in this template used to hang off ipv6_support
|
||||
* alone. That flag only says whether the user *wants* IPv6 handled; it
|
||||
* says nothing about whether we can tell a mainland IPv6 destination from
|
||||
* a foreign one. That test is the homeproxy_mainland_addr_v6 set, and the
|
||||
* set is filled straight from resources/china_ip6.txt - so an install whose
|
||||
* list is missing, empty or entirely malformed produced a *syntactically
|
||||
* valid but permanently empty* set. `ip6 daddr @homeproxy_mainland_addr_v6
|
||||
* counter return` then matched nothing, and every mainland IPv6 connection
|
||||
* fell through to the proxy hop at the bottom of the chain: a silent,
|
||||
* unlogged inversion of the mode's promise, and the reason a user who
|
||||
* enables IPv6 finds Chinese sites taking the long way round.
|
||||
*
|
||||
* nft happily accepts `elements = { }`, so nothing downstream could notice.
|
||||
* The count is taken here, once, with the same isValidCIDR() the set
|
||||
* rendering uses - a list of entries this function rejects would render an
|
||||
* empty set too, so counting something else would not be the same test.
|
||||
*
|
||||
* v6_handled is what every v6 branch below now keys off. When the list is
|
||||
* unusable it is false, and the ruleset degrades to exactly the IPv6-off
|
||||
* shape: IPv6 is not intercepted, not marked, not redirected. That is a
|
||||
* deliberate choice of failure direction. The alternative - keep
|
||||
* intercepting and proxy everything - trades a wrong-but-working path for
|
||||
* a leaked one, and the user cannot see either from the client. Passing
|
||||
* IPv6 through unproxied is the state the router was in before this package
|
||||
* touched it, it is recoverable (fix the list, reload), and it is announced:
|
||||
* see the comment on the dstnat jump below and the warning
|
||||
* hp_prepare_runtime_files() logs on every start. */
|
||||
let cn_ipv6_count = 0;
|
||||
for (let entry in split(trim(readfile(resources_dir + '/china_ip6.txt')), /[\r\n]/))
|
||||
if (isValidCIDR(entry, 6))
|
||||
cn_ipv6_count++;
|
||||
|
||||
const cn_ipv6_ready = (cn_ipv6_count > 0);
|
||||
const v6_handled = (ipv6_support === '1') && cn_ipv6_ready;
|
||||
|
||||
/* The degraded-state warning, as a ready-made nft comment suffix.
|
||||
*
|
||||
* A real `comment` rather than a `#` line on purpose: fw4 keeps `#` lines
|
||||
* only in the generated file, while a rule comment is part of the rule and
|
||||
* survives into `nft list ruleset` - which is where someone debugging "why
|
||||
* is my IPv6 not going through the proxy" actually looks.
|
||||
*
|
||||
* Computed once and interpolated into all three jump rules rather than
|
||||
* wrapped around each of them: the rule text is identical either way, only
|
||||
* the comment differs, and one string is one place to change if the wording
|
||||
* ever has to. */
|
||||
const v6_warn_comment = ((ipv6_support === '1') && !cn_ipv6_ready)
|
||||
? ' comment "!homeproxy-pro: WARNING china_ip6.txt has 0 usable prefixes - IPv6 is NOT proxied, run update_resources.sh china_ip6"'
|
||||
: '';
|
||||
|
||||
let self_mark, redirect_port,
|
||||
tproxy_port, tproxy_mark,
|
||||
tun_name, tun_mark;
|
||||
@@ -232,7 +284,7 @@ set homeproxy_local_addr_v4 {
|
||||
240.0.0.0/4
|
||||
}
|
||||
}
|
||||
{% if (ipv6_support === '1'): %}
|
||||
{% if (v6_handled): %}
|
||||
set homeproxy_local_addr_v6 {
|
||||
type ipv6_addr
|
||||
flags interval
|
||||
@@ -261,13 +313,13 @@ set homeproxy_gfw_list_v4 {
|
||||
flags interval
|
||||
auto-merge
|
||||
}
|
||||
{% if (ipv6_support === '1'): %}
|
||||
{% if (v6_handled): %}
|
||||
set homeproxy_gfw_list_v6 {
|
||||
type ipv6_addr
|
||||
flags interval
|
||||
auto-merge
|
||||
}
|
||||
{% endif /* ipv6_support */ %}
|
||||
{% endif /* v6_handled */ %}
|
||||
{% elif (match(routing_mode, /mainland_china/) || bypass_cn_traffic === '1'): %}
|
||||
set homeproxy_mainland_addr_v4 {
|
||||
type ipv4_addr
|
||||
@@ -281,7 +333,7 @@ set homeproxy_mainland_addr_v4 {
|
||||
{% endfor %}
|
||||
}
|
||||
}
|
||||
{% if ((ipv6_support === '1') || china_dns_server): %}
|
||||
{% if (v6_handled): %}
|
||||
set homeproxy_mainland_addr_v6 {
|
||||
type ipv6_addr
|
||||
flags interval
|
||||
@@ -294,7 +346,7 @@ set homeproxy_mainland_addr_v6 {
|
||||
{% endfor %}
|
||||
}
|
||||
}
|
||||
{% endif /* ipv6_support */ %}
|
||||
{% endif /* v6_handled */ %}
|
||||
{% endif /* routing_mode */ %}
|
||||
|
||||
{# WAN ACL addresses #}
|
||||
@@ -307,7 +359,7 @@ set homeproxy_wan_proxy_addr_v4 {
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
{% if (ipv6_support === '1'): %}
|
||||
{% if (v6_handled): %}
|
||||
set homeproxy_wan_proxy_addr_v6 {
|
||||
type ipv6_addr
|
||||
flags interval
|
||||
@@ -316,7 +368,7 @@ set homeproxy_wan_proxy_addr_v6 {
|
||||
elements = { {{ ipv6_to_nftarr(control_info.wan_proxy_ipv6_ips) }} }
|
||||
{% endif /* wan_proxy_ipv6_ips*/ %}
|
||||
}
|
||||
{% endif /* ipv6_support */ %}
|
||||
{% endif /* v6_handled */ %}
|
||||
|
||||
set homeproxy_wan_direct_addr_v4 {
|
||||
type ipv4_addr
|
||||
@@ -327,7 +379,7 @@ set homeproxy_wan_direct_addr_v4 {
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
{% if (ipv6_support === '1'): %}
|
||||
{% if (v6_handled): %}
|
||||
set homeproxy_wan_direct_addr_v6 {
|
||||
type ipv6_addr
|
||||
flags interval
|
||||
@@ -336,7 +388,7 @@ set homeproxy_wan_direct_addr_v6 {
|
||||
elements = { {{ ipv6_to_nftarr(control_info.wan_direct_ipv6_ips) }} }
|
||||
{% endif /* wan_direct_ipv6_ips */ %}
|
||||
}
|
||||
{% endif /* ipv6_support */ %}
|
||||
{% endif /* v6_handled */ %}
|
||||
|
||||
{% if (routing_port): %}
|
||||
set homeproxy_routing_port {
|
||||
@@ -356,7 +408,7 @@ chain dstnat {
|
||||
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
|
||||
meta nfproto { {{ (v6_handled) ? 'ipv4, ipv6' : 'ipv4' }} } meta l4proto tcp jump homeproxy_redirect_lanac{{ v6_warn_comment }}
|
||||
{% endif /* proxy_mode */ %}
|
||||
}
|
||||
|
||||
@@ -410,12 +462,12 @@ 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'): %}
|
||||
{% if (v6_handled): %}
|
||||
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'): %}
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr @homeproxy_local_addr_v6 counter return
|
||||
{% endif %}
|
||||
|
||||
@@ -432,25 +484,25 @@ chain homeproxy_redirect {
|
||||
{% endif /* routing_mode */ %}
|
||||
|
||||
ip daddr @homeproxy_wan_direct_addr_v4 counter return
|
||||
{% if (ipv6_support === '1'): %}
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr @homeproxy_wan_direct_addr_v6 counter return
|
||||
{% endif /* ipv6_support */ %}
|
||||
{% endif /* v6_handled */ %}
|
||||
|
||||
{% if (routing_mode === 'gfwlist' && dnsmasq_nftset()): %}
|
||||
ip daddr != @homeproxy_gfw_list_v4 counter return
|
||||
{% if (ipv6_support === '1'): %}
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr != @homeproxy_gfw_list_v6 counter return
|
||||
{% endif /* ipv6_support */ %}
|
||||
{% endif /* v6_handled */ %}
|
||||
{% elif (routing_mode === 'bypass_mainland_china' || bypass_cn_traffic === '1'): %}
|
||||
ip daddr @homeproxy_mainland_addr_v4 counter return
|
||||
{% if (ipv6_support === '1'): %}
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr @homeproxy_mainland_addr_v6 counter return
|
||||
{% endif /* ipv6_support */ %}
|
||||
{% endif /* v6_handled */ %}
|
||||
{% elif (routing_mode === 'proxy_mainland_china'): %}
|
||||
ip daddr != @homeproxy_mainland_addr_v4 counter return
|
||||
{% if (ipv6_support === '1'): %}
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr != @homeproxy_mainland_addr_v6 counter return
|
||||
{% endif /* ipv6_support */ %}
|
||||
{% endif /* v6_handled */ %}
|
||||
{% endif /* routing_mode */ %}
|
||||
|
||||
{% if (!isEmpty(control_info.lan_gaming_mode_ipv4_ips)): %}
|
||||
@@ -468,7 +520,7 @@ chain homeproxy_redirect {
|
||||
|
||||
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
|
||||
meta nfproto { {{ (v6_handled) ? 'ipv4, ipv6' : 'ipv4' }} } meta l4proto tcp jump homeproxy_redirect
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
@@ -476,7 +528,7 @@ chain homeproxy_output_redir {
|
||||
{% 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'): %}
|
||||
{% if (v6_handled): %}
|
||||
meta l4proto udp meta mark set {{ tproxy_mark }} tproxy ip6 to [::1]:{{ tproxy_port }} counter accept
|
||||
{% endif %}
|
||||
}
|
||||
@@ -531,12 +583,12 @@ chain homeproxy_mangle_lanac {
|
||||
|
||||
chain homeproxy_mangle_prerouting {
|
||||
ip daddr @homeproxy_wan_proxy_addr_v4 counter goto homeproxy_mangle_tproxy_port
|
||||
{% if (ipv6_support === '1'): %}
|
||||
{% if (v6_handled): %}
|
||||
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'): %}
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr @homeproxy_local_addr_v6 counter return
|
||||
{% endif %}
|
||||
|
||||
@@ -553,29 +605,29 @@ chain homeproxy_mangle_prerouting {
|
||||
{% endif /* routing_mode */ %}
|
||||
|
||||
ip daddr @homeproxy_wan_direct_addr_v4 counter return
|
||||
{% if (ipv6_support === '1'): %}
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr @homeproxy_wan_direct_addr_v6 counter return
|
||||
{% endif /* ipv6_support */ %}
|
||||
{% endif /* v6_handled */ %}
|
||||
|
||||
{% if (routing_mode === 'gfwlist' && dnsmasq_nftset()): %}
|
||||
ip daddr != @homeproxy_gfw_list_v4 counter return
|
||||
{% if (ipv6_support === '1'): %}
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr != @homeproxy_gfw_list_v6 counter return
|
||||
{% endif /* ipv6_support */ %}
|
||||
{% endif /* v6_handled */ %}
|
||||
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'): %}
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr @homeproxy_mainland_addr_v6 counter return
|
||||
{% endif /* ipv6_support */ %}
|
||||
{% endif /* v6_handled */ %}
|
||||
{% 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'): %}
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr != @homeproxy_mainland_addr_v6 counter return
|
||||
{% endif /* ipv6_support */ %}
|
||||
{% endif /* v6_handled */ %}
|
||||
{% endif /* routing_mode */ %}
|
||||
|
||||
{% if (!isEmpty(control_info.lan_gaming_mode_ipv4_ips)): %}
|
||||
@@ -595,46 +647,46 @@ 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'): %}
|
||||
{% if (v6_handled): %}
|
||||
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'): %}
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr @homeproxy_local_addr_v6 counter return
|
||||
{% endif %}
|
||||
|
||||
ip daddr @homeproxy_wan_direct_addr_v4 counter return
|
||||
{% if (ipv6_support === '1'): %}
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr @homeproxy_wan_direct_addr_v6 counter return
|
||||
{% endif /* ipv6_support */ %}
|
||||
{% endif /* v6_handled */ %}
|
||||
|
||||
{% if (routing_mode === 'gfwlist' && dnsmasq_nftset()): %}
|
||||
ip daddr != @homeproxy_gfw_list_v4 counter return
|
||||
{% if (ipv6_support === '1'): %}
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr != @homeproxy_gfw_list_v6 counter return
|
||||
{% endif /* ipv6_support */ %}
|
||||
{% endif /* v6_handled */ %}
|
||||
{% elif (routing_mode === 'bypass_mainland_china' || bypass_cn_traffic === '1'): %}
|
||||
ip daddr @homeproxy_mainland_addr_v4 counter return
|
||||
{% if (ipv6_support === '1'): %}
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr @homeproxy_mainland_addr_v6 counter return
|
||||
{% endif /* ipv6_support */ %}
|
||||
{% endif /* v6_handled */ %}
|
||||
{% elif (routing_mode === 'proxy_mainland_china'): %}
|
||||
ip daddr != @homeproxy_mainland_addr_v4 counter return
|
||||
{% if (ipv6_support === '1'): %}
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr != @homeproxy_mainland_addr_v6 counter return
|
||||
{% endif /* ipv6_support */ %}
|
||||
{% endif /* v6_handled */ %}
|
||||
{% 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
|
||||
meta nfproto { {{ (v6_handled) ? 'ipv4, ipv6' : 'ipv4' }} } meta l4proto udp jump homeproxy_mangle_lanac{{ v6_warn_comment }}
|
||||
}
|
||||
|
||||
chain mangle_output {
|
||||
meta nfproto { {{ (ipv6_support === '1') ? 'ipv4, ipv6' : 'ipv4' }} } meta l4proto udp jump homeproxy_mangle_output
|
||||
meta nfproto { {{ (v6_handled) ? 'ipv4, ipv6' : 'ipv4' }} } meta l4proto udp jump homeproxy_mangle_output
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
@@ -690,12 +742,12 @@ 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'): %}
|
||||
{% if (v6_handled): %}
|
||||
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'): %}
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr @homeproxy_local_addr_v6 counter return
|
||||
{% endif %}
|
||||
|
||||
@@ -720,23 +772,23 @@ chain homeproxy_mangle_tun {
|
||||
|
||||
{% if (routing_mode === 'gfwlist' && dnsmasq_nftset()): %}
|
||||
ip daddr != @homeproxy_gfw_list_v4 counter return
|
||||
{% if (ipv6_support === '1'): %}
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr != @homeproxy_gfw_list_v6 counter return
|
||||
{% endif /* ipv6_support */ %}
|
||||
{% endif /* v6_handled */ %}
|
||||
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'): %}
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr @homeproxy_mainland_addr_v6 counter return
|
||||
{% endif /* ipv6_support */ %}
|
||||
{% endif /* v6_handled */ %}
|
||||
{% 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'): %}
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr != @homeproxy_mainland_addr_v6 counter return
|
||||
{% endif /* ipv6_support */ %}
|
||||
{% endif /* v6_handled */ %}
|
||||
{% endif /* routing_mode */ %}
|
||||
|
||||
{% if (!isEmpty(control_info.lan_gaming_mode_ipv4_ips)): %}
|
||||
@@ -753,10 +805,10 @@ chain homeproxy_mangle_tun {
|
||||
}
|
||||
|
||||
chain mangle_prerouting {
|
||||
meta nfproto { {{ (ipv6_support === '1') ? 'ipv4, ipv6' : 'ipv4' }} } meta l4proto { {{ (proxy_mode === 'tun') ? 'tcp, udp' : 'udp' }} } jump homeproxy_mangle_lanac
|
||||
meta nfproto { {{ (v6_handled) ? 'ipv4, ipv6' : 'ipv4' }} } meta l4proto { {{ (proxy_mode === 'tun') ? 'tcp, udp' : 'udp' }} } jump homeproxy_mangle_lanac{{ v6_warn_comment }}
|
||||
}
|
||||
|
||||
chain mangle_output {
|
||||
meta nfproto { {{ (ipv6_support === '1') ? 'ipv4, ipv6' : 'ipv4' }} } meta l4proto { {{ (proxy_mode === 'tun') ? 'tcp, udp' : 'udp' }} } jump homeproxy_mangle_tun
|
||||
meta nfproto { {{ (v6_handled) ? 'ipv4, ipv6' : 'ipv4' }} } meta l4proto { {{ (proxy_mode === 'tun') ? 'tcp, udp' : 'udp' }} } jump homeproxy_mangle_tun
|
||||
}
|
||||
{% endif %}
|
||||
@@ -25,7 +25,7 @@
|
||||
'use strict';
|
||||
|
||||
import { connect } from 'ubus';
|
||||
import { mkdtemp, readfile, writefile } from 'fs';
|
||||
import { lstat, mkdtemp, readfile, writefile } from 'fs';
|
||||
|
||||
import { Loader } from './config/loader.uc';
|
||||
import { generate } from './generator/client.uc';
|
||||
@@ -52,7 +52,36 @@ function resolve_env(dm) {
|
||||
const env = {
|
||||
wan_dns: (ubus?.call('network.interface', 'status', {'interface': 'wan'}))?.['dns-server']?.[0],
|
||||
direct_domain_list: [],
|
||||
proxy_domain_list: []
|
||||
proxy_domain_list: [],
|
||||
/* Whether the generated mainland rule-set for IPv6 is on disk.
|
||||
*
|
||||
* generator/ may not stat anything (guard 27), and this is exactly
|
||||
* the kind of environment a generator is supposed to be handed:
|
||||
* geoip-cn.srs and china_ip4.json carry no IPv6, so china_ip6.json -
|
||||
* written by hp_prepare_runtime_files from the list the resource
|
||||
* updater maintains - is the only thing that lets the route and DNS
|
||||
* halves recognise a mainland IPv6 destination. When it is absent
|
||||
* (the list is missing, or had no usable prefix when the file was
|
||||
* last written) both halves have to stay silent about IPv6 instead
|
||||
* of naming a rule-set that is not there: a `rule_set:` pointing at
|
||||
* a missing file makes sing-box reject the entire config, which the
|
||||
* health gate turns into a rollback and an unproxied network. The
|
||||
* firewall has already degraded to passing IPv6 through, with a
|
||||
* warning in the ruleset and the log.
|
||||
*
|
||||
* lstat(), not the two-argument access(). fs.access() on this ucode
|
||||
* build answers only in its one-argument form: access(path) returns
|
||||
* true for an existing path and null for a missing one, while
|
||||
* access(path, mode) returns null either way. The two-argument form
|
||||
* is therefore the worst possible choice here - it reports every
|
||||
* file as missing, so this flag would be permanently false and the
|
||||
* whole IPv6 split would stay switched off with nothing logged. The
|
||||
* one-argument call sites elsewhere in the tree (homeproxy-pro.uc's
|
||||
* cleanup_exec_dir) are correct and must stay as they are.
|
||||
* lstat() is used because it is unambiguous - there is no second
|
||||
* argument to get wrong - and because the neighbouring stderr-size
|
||||
* check in homeproxy-pro.uc already reads sizes through it. */
|
||||
china_ip6_ready: lstat(HP_DIR + '/resources/china_ip6.json') !== null
|
||||
};
|
||||
|
||||
if (routing_mode !== 'custom') {
|
||||
|
||||
@@ -131,6 +131,13 @@ export function build_context(dm, env) {
|
||||
const ctx = {
|
||||
routing_mode, proxy_mode, ipv6_support, log_level,
|
||||
proxy_fallback,
|
||||
/* Resolved by the caller, which is the only layer allowed to look at
|
||||
* the filesystem (guard 27). The route rule and the DNS cn-fallback
|
||||
* match must agree on it: they reference the same generated rule-set,
|
||||
* and one of them naming a tag the other never declared fails the
|
||||
* whole config. Default false - an absent env must not turn into an
|
||||
* optimistic "assume the file is there". */
|
||||
china_ip6_ready: (env?.china_ip6_ready === true),
|
||||
self_mark, ntp_server, dns_port, mixed_port,
|
||||
redirect_port, tproxy_port,
|
||||
tun_name, tun_addr4, tun_addr6, tun_mtu,
|
||||
|
||||
@@ -291,9 +291,24 @@ function append_proxy_dns(config, dm, ctx) {
|
||||
server: 'main-dns',
|
||||
tag: 'cn-fallback'
|
||||
});
|
||||
/* The response is matched by the addresses the upstream returned,
|
||||
* and geoip-cn has no IPv6 in it, so with IPv6 support on a
|
||||
* mainland domain that resolved to an AAAA address was never
|
||||
* recognised as mainland and its answer kept coming from the
|
||||
* proxy resolver. Adding the v6 rule-set here is what makes the
|
||||
* DNS half of the split agree with the route half, which
|
||||
* classifies the same destinations through the same list
|
||||
* (route.uc: china-ip6). Both tags have to be declared before
|
||||
* this rule runs, which is why the list is built here rather than
|
||||
* inlined: a rule_set naming an undeclared tag fails the config.
|
||||
* china-ip6 is only declared when its file exists, so it is only
|
||||
* named here under the same condition. */
|
||||
const cn_fallback_sets = ['geoip-cn'];
|
||||
if (ctx.ipv6_support === '1' && ctx.china_ip6_ready)
|
||||
push(cn_fallback_sets, 'china-ip6');
|
||||
push(config.dns.rules, {
|
||||
match_response: 'cn-fallback',
|
||||
rule_set: 'geoip-cn',
|
||||
rule_set: cn_fallback_sets,
|
||||
action: 'route',
|
||||
server: 'china-dns'
|
||||
});
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
|
||||
import { isEmpty, strToInt, strToTime, strToBool, parse_port, HP_DIR } from '../homeproxy-pro.uc';
|
||||
|
||||
|
||||
import { get_outbound, get_resolver, get_ruleset, get_direct_override } from './common.uc';
|
||||
|
||||
/* --- shared initial block (every routing mode) ------------------------- */
|
||||
@@ -136,6 +135,25 @@ function build_route_proxy(config, dm, ctx, direct_overrides) {
|
||||
action: 'route',
|
||||
outbound: 'direct-out'
|
||||
});
|
||||
|
||||
/* IPv6 half of the same split, and the reason a mainland site opened
|
||||
* over IPv6 used to go through the proxy even with IPv6 support on.
|
||||
* geoip-cn.srs carries no IPv6 and china_ip4.json is generated from an
|
||||
* IPv4 list, so a destination that resolved to an AAAA address matched
|
||||
* neither and fell through to `final` - which in this mode is main-out.
|
||||
* The rule is emitted in both mainland modes (unlike china-ip, which
|
||||
* proxy_mainland_china does not need twice because geoip-cn already
|
||||
* picks the mainland side there): with no IPv6 in geoip-cn there is
|
||||
* nothing left to pick it, so the side has to be named explicitly.
|
||||
* `prefer_ipv4` on the resolve rule above is what lets both families
|
||||
* through - it biases, it does not filter. */
|
||||
if (ctx.ipv6_support === '1' && ctx.china_ip6_ready) {
|
||||
push(config.route.rules, {
|
||||
rule_set: 'china-ip6',
|
||||
action: 'route',
|
||||
outbound: ctx.proxy_fallback ? 'direct-out' : 'main-out'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* Main UDP out */
|
||||
@@ -250,15 +268,27 @@ function build_route_proxy(config, dm, ctx, direct_overrides) {
|
||||
* from, so the two cannot drift apart. `type: local` is watched by
|
||||
* sing-box with fswatch, so when the resource updater replaces the
|
||||
* file the running instance reloads it in place - no restart and,
|
||||
* more importantly, no second copy of the list to keep in sync.
|
||||
* China IPv6 is deliberately absent: the firewall only fills the v6
|
||||
* set when ipv6_support is on, and the client refuses to resolve
|
||||
* AAAA when it is off. */
|
||||
* more importantly, no second copy of the list to keep in sync. */
|
||||
push(config.route.rule_set, {
|
||||
type: 'local',
|
||||
tag: 'china-ip',
|
||||
path: HP_DIR + '/resources/china_ip4.json'
|
||||
});
|
||||
|
||||
/* The IPv6 counterpart, from china_ip6.txt - the same list the
|
||||
* firewall renders homeproxy_mainland_addr_v6 from, and the only
|
||||
* IPv6 mainland data either side of the split has. Declared only
|
||||
* when the file is actually there: a rule_set pointing at a missing
|
||||
* file takes the whole config down with it, and a router whose v6
|
||||
* list is unusable must still start (the firewall has already
|
||||
* degraded to passing IPv6 through, with a warning). */
|
||||
if (ctx.ipv6_support === '1' && ctx.china_ip6_ready) {
|
||||
push(config.route.rule_set, {
|
||||
type: 'local',
|
||||
tag: 'china-ip6',
|
||||
path: HP_DIR + '/resources/china_ip6.json'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (isEmpty(config.route.rule_set))
|
||||
|
||||
@@ -166,7 +166,7 @@ hp_prepare_runtime_files() {
|
||||
local client_enabled="$4"
|
||||
local server_enabled="$5"
|
||||
|
||||
# The route rule that keeps mainland destinations direct matches a local
|
||||
# The route rules that keep mainland destinations direct match a local
|
||||
# rule-set generated from china_ip4.txt, the same list the firewall
|
||||
# renders its nft set from, so both sides decide from one source. It is
|
||||
# regenerated here as well as by the resource updater: an install that
|
||||
@@ -184,6 +184,40 @@ hp_prepare_runtime_files() {
|
||||
fi
|
||||
fi
|
||||
|
||||
# IPv6 half of the same arrangement, and the only place the "IPv6 is on
|
||||
# but cannot be classified" condition is reported in a place a user reads
|
||||
# without tcpdump. geoip-cn.srs and china_ip4.json are both IPv4-only, so
|
||||
# without a v6 rule-set a mainland destination reached over IPv6 matches
|
||||
# no route rule at all and falls through to `final` - i.e. to the proxy.
|
||||
# The firewall would normally have returned it first, but that set is
|
||||
# filled from the same china_ip6.txt, so a list that is missing or has no
|
||||
# usable prefix leaves neither side able to classify it. Say so plainly
|
||||
# instead: the generated ruleset also carries the same text as a rule
|
||||
# comment, so `nft list ruleset` shows the degraded state too.
|
||||
local ipv6_support cn_ipv6
|
||||
config_get_bool ipv6_support "config" "ipv6_support" "0"
|
||||
cn_ipv6=0
|
||||
if [ -f "$hp_dir/resources/china_ip6.txt" ]; then
|
||||
cn_ipv6="$(grep -cE '^[0-9a-fA-F:]+:[0-9a-fA-F:]*(/[0-9]{1,3})?[[:space:]]*$' \
|
||||
"$hp_dir/resources/china_ip6.txt" 2>"/dev/null")"
|
||||
[ -n "$cn_ipv6" ] || cn_ipv6=0
|
||||
if [ "$cn_ipv6" -gt 0 ] && ucode -S "$hp_dir/scripts/runtime/china_ip_ruleset.uc" \
|
||||
"$hp_dir/resources/china_ip6.txt" "$hp_dir/resources/china_ip6.json" >>"$LOG_PATH" 2>&1; then
|
||||
chown sing-box:sing-box "$hp_dir/resources/china_ip6.json" 2>"/dev/null" \
|
||||
|| log "Warning: failed to hand ${hp_dir}/resources/china_ip6.json to sing-box."
|
||||
else
|
||||
# Only reached when the source is unusable, which china_ip_ruleset.uc
|
||||
# reports as "no usable CIDR entries". Keep the previous file: an
|
||||
# empty one would make the route-side set match nothing, which is the
|
||||
# same silent inversion this whole branch exists to prevent.
|
||||
log "Warning: could not generate ${hp_dir}/resources/china_ip6.json; the route side keeps the previous list."
|
||||
fi
|
||||
fi
|
||||
if [ "$ipv6_support" -eq 1 ] && [ "$cn_ipv6" -eq 0 ]; then
|
||||
log "WARNING: IPv6 support is ON but ${hp_dir}/resources/china_ip6.txt has 0 usable prefixes."
|
||||
log "WARNING: mainland IPv6 cannot be told apart from foreign IPv6, so IPv6 is passed through UNPROXIED (both sides of the split degrade). Run: ${hp_dir}/scripts/update_resources.sh china_ip6"
|
||||
fi
|
||||
|
||||
# cache_file is enabled for bypass_mainland_china and custom alike
|
||||
# (generator/common.uc:attachExperimental). The file has to exist before
|
||||
# the client jail binds it, or sing-box cannot create it in there.
|
||||
|
||||
@@ -201,15 +201,27 @@ check_list_update() {
|
||||
# not fatal: the previous rule-set is still valid and the firewall
|
||||
# has already switched to the new list, which is exactly the state
|
||||
# the next run (or the next service start) repairs.
|
||||
if [ "$listtype" = "china_ip4" ]; then
|
||||
#
|
||||
# china_ip6 is in the same boat for the same reason: geoip-cn.srs and
|
||||
# china_ip4.json carry no IPv6 at all, so a mainland destination
|
||||
# reached over v6 matches no route rule and falls through to `final`
|
||||
# - the proxy - unless there is a v6 rule-set of our own.
|
||||
case "$listtype" in
|
||||
"china_ip4"|"china_ip6")
|
||||
if ucode -S "$SCRIPT_DIR/runtime/china_ip_ruleset.uc" \
|
||||
"$RESOURCES_DIR/china_ip4.txt" "$RESOURCES_DIR/china_ip4.json" >>"$LOG_PATH" 2>&1; then
|
||||
log "[CHINA_IP4] Route-side rule-set regenerated."
|
||||
chown sing-box:sing-box "$RESOURCES_DIR/china_ip4.json" 2>"/dev/null"
|
||||
"$RESOURCES_DIR/$listtype.txt" "$RESOURCES_DIR/$listtype.json" >>"$LOG_PATH" 2>&1; then
|
||||
log "[$(to_upper "$listtype")] Route-side rule-set regenerated."
|
||||
chown sing-box:sing-box "$RESOURCES_DIR/$listtype.json" 2>"/dev/null"
|
||||
else
|
||||
log "[CHINA_IP4] Warning: could not regenerate china_ip4.json; the route side keeps the previous list."
|
||||
# The helper exits non-zero on an empty or fully malformed
|
||||
# list, and deliberately leaves the previous .json in place.
|
||||
# Writing an empty one would be worse than not writing any:
|
||||
# the rule would then match nothing, which is the silent
|
||||
# "mainland IPv6 goes through the proxy" inversion.
|
||||
log "[$(to_upper "$listtype")] Warning: could not regenerate $listtype.json (list has no usable CIDR entry?); the route side keeps the previous list."
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
else
|
||||
rm -f "$RUN_DIR/$listname"
|
||||
log "[$(to_upper "$listtype")] Failed to install update (mv failed)."
|
||||
|
||||
Reference in new issue
Block a user