Add priority bypass routing action
Replace user-facing direct routing action with bypass, add priority nft bypass handling, real DNS for bypass domains, migration cleanup, and the related runtime/frontend tests. Also includes the previously pending runtime log hygiene cleanup and regression coverage that was already in the working tree.
This commit is contained in:
@@ -84,13 +84,13 @@ function uiValues(field) {
|
||||
return [...result].sort();
|
||||
}
|
||||
|
||||
function assertCurrentKeepsStableValues(name, required = []) {
|
||||
function assertCurrentKeepsStableValues(name, required = [], retired = []) {
|
||||
const field = matrix.fields.find((item) => item.name === name);
|
||||
if (!field) fail(`expected enum field is absent from matrix: ${name}`);
|
||||
|
||||
const stableValues = uiValues(field.stable);
|
||||
const currentValues = uiValues(field.current);
|
||||
const missingStable = stableValues.filter((value) => !currentValues.includes(value));
|
||||
const missingStable = stableValues.filter((value) => !currentValues.includes(value) && !retired.includes(value));
|
||||
if (missingStable.length) {
|
||||
fail(`current UI contract for ${name} is missing stable values: ${missingStable.join(", ")}`);
|
||||
}
|
||||
@@ -101,7 +101,7 @@ function assertCurrentKeepsStableValues(name, required = []) {
|
||||
}
|
||||
}
|
||||
|
||||
assertCurrentKeepsStableValues("action", ["proxy", "vpn", "direct", "block", "zapret", "zapret2", "byedpi", "outbound"]);
|
||||
assertCurrentKeepsStableValues("action", ["proxy", "vpn", "bypass", "block", "zapret", "zapret2", "byedpi", "outbound"], ["direct"]);
|
||||
assertCurrentKeepsStableValues("protocol", ["tailscale", "vless", "vmess", "trojan", "shadowsocks", "hysteria2", "socks", "mtproto", "json_inbound"]);
|
||||
assertCurrentKeepsStableValues("security", ["reality", "tls", "none"]);
|
||||
assertCurrentKeepsStableValues("transport", ["tcp", "ws", "grpc", "http", "httpupgrade", "xhttp"]);
|
||||
|
||||
@@ -30,7 +30,8 @@ const fixture = {
|
||||
settings: {
|
||||
'.name': 'settings',
|
||||
'.type': 'settings',
|
||||
download_lists_via_proxy: '1'
|
||||
download_lists_via_proxy: '1',
|
||||
routing_excluded_ips: [ '192.0.2.0/24' ]
|
||||
},
|
||||
rule: [
|
||||
{
|
||||
@@ -70,6 +71,18 @@ const fixture = {
|
||||
urltest_enabled: '1',
|
||||
detect_server_country: '0'
|
||||
},
|
||||
{
|
||||
'.name': 'legacy-direct',
|
||||
'.type': 'section',
|
||||
action: 'direct',
|
||||
ip_cidr: [ '198.51.100.0/24' ]
|
||||
},
|
||||
{
|
||||
'.name': 'legacy-exclusion',
|
||||
'.type': 'section',
|
||||
connection_type: 'exclusion',
|
||||
ip_cidr: [ '203.0.113.0/24' ]
|
||||
},
|
||||
{
|
||||
'.name': 'legacy-zap',
|
||||
'.type': 'section',
|
||||
@@ -121,6 +134,7 @@ assert(config.settings.update_interval === '1d', 'missing update_interval should
|
||||
assert(config.settings.download_lists_via_proxy === '1', 'download_lists_via_proxy should be preserved');
|
||||
assert(config.settings.download_subscriptions_via_proxy === '1', 'download_subscriptions_via_proxy should be copied');
|
||||
assert(config.settings.download_components_via_proxy === '1', 'download_components_via_proxy should be copied');
|
||||
absent(config.settings, 'routing_excluded_ips', 'settings');
|
||||
|
||||
const legacyUrl = sections['legacy-url'];
|
||||
assert(legacyUrl['.type'] === 'section', 'legacy rule should become section');
|
||||
@@ -154,6 +168,13 @@ assert(legacyUrltest.detect_server_country === 'flag_emoji', 'detect server coun
|
||||
assert(JSON.stringify(legacyUrltest.selector_proxy_links) === JSON.stringify(['vmess://a', 'trojan://b']), 'urltest links deduped');
|
||||
absent(legacyUrltest, 'urltest_proxy_links', 'legacy-urltest');
|
||||
|
||||
const legacyDirect = sections['legacy-direct'];
|
||||
assert(legacyDirect.action === 'bypass', 'direct action migrated to bypass');
|
||||
|
||||
const legacyExclusion = sections['legacy-exclusion'];
|
||||
assert(legacyExclusion.action === 'bypass', 'legacy exclusion connection type migrated to bypass');
|
||||
absent(legacyExclusion, 'connection_type', 'legacy-exclusion');
|
||||
|
||||
const legacyZap = sections['legacy-zap'];
|
||||
assert(legacyZap.nfqws_opt === process.env.ZAPRET_DEFAULT_NFQWS_OPT, 'zapret legacy default migrated');
|
||||
assert(legacyZap.byedpi_cmd_opts === '--legacy-bye', 'cmd_opts copied to byedpi_cmd_opts');
|
||||
@@ -172,11 +193,15 @@ NODE
|
||||
|
||||
cat >"$WORK_DIR/runtime-migrate.state" <<'EOF_UCI'
|
||||
podkop-plus.settings=settings
|
||||
podkop-plus.settings.routing_excluded_ips=192.0.2.0/24
|
||||
podkop-plus.legacy=rule
|
||||
podkop-plus.legacy.enabled=1
|
||||
podkop-plus.legacy.connection_type=proxy
|
||||
podkop-plus.legacy.proxy_config_type=url
|
||||
podkop-plus.legacy.proxy_string=vless://one
|
||||
podkop-plus.old_direct=section
|
||||
podkop-plus.old_direct.enabled=1
|
||||
podkop-plus.old_direct.action=direct
|
||||
EOF_UCI
|
||||
mkdir -p "$WORK_DIR/runtime" "$WORK_DIR/persistent-cache"
|
||||
: >"$WORK_DIR/runtime-migrate.log"
|
||||
@@ -193,8 +218,13 @@ grep -Fxq 'podkop-plus.legacy=section' "$WORK_DIR/runtime-migrate.state" ||
|
||||
fail "runtime migration must convert legacy rule type through core.uci"
|
||||
grep -Fxq 'podkop-plus.legacy.action=proxy' "$WORK_DIR/runtime-migrate.state" ||
|
||||
fail "runtime migration must write migrated action through core.uci"
|
||||
grep -Fxq 'podkop-plus.old_direct.action=bypass' "$WORK_DIR/runtime-migrate.state" ||
|
||||
fail "runtime migration must convert direct action to bypass through core.uci"
|
||||
grep -Fxq 'podkop-plus.legacy.selector_proxy_links=vless://one' "$WORK_DIR/runtime-migrate.state" ||
|
||||
fail "runtime migration must write migrated list option through core.uci"
|
||||
if grep -Fq 'podkop-plus.settings.routing_excluded_ips=' "$WORK_DIR/runtime-migrate.state"; then
|
||||
fail "runtime migration must delete removed routing_excluded_ips through core.uci"
|
||||
fi
|
||||
if grep -Fq 'podkop-plus.legacy.proxy_string=' "$WORK_DIR/runtime-migrate.state"; then
|
||||
fail "runtime migration must delete legacy proxy_string through core.uci"
|
||||
fi
|
||||
|
||||
@@ -52,6 +52,6 @@ assert_rejects "empty target" "no download section is selected" "" 0 0 0 proxy 1
|
||||
assert_rejects "missing target" "references missing rule 'missing'" missing 0 0 0 proxy 1 proxy
|
||||
assert_rejects "disabled target" "references disabled rule 'proxy'" proxy 0 0 0 proxy 0 proxy
|
||||
assert_rejects "provider missing" "cannot provide an outbound" zap 0 0 0 zap 1 zapret
|
||||
assert_rejects "unsupported action" "cannot provide an outbound" direct 0 0 0 direct 1 direct
|
||||
assert_rejects "unsupported action" "cannot provide an outbound" bypass 0 0 0 bypass 1 bypass
|
||||
|
||||
printf 'config validator download section regression checks passed\n'
|
||||
|
||||
@@ -83,6 +83,13 @@ cat >"$WORK_DIR/valid.json" <<'JSON'
|
||||
"outbound_detour_enabled": "1",
|
||||
"outbound_detour_section": "detour"
|
||||
},
|
||||
{
|
||||
".name": "bypass",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "bypass",
|
||||
"domain_suffix": [ "bypass.example" ]
|
||||
},
|
||||
{
|
||||
".name": "zap",
|
||||
".type": "section",
|
||||
@@ -96,6 +103,16 @@ JSON
|
||||
|
||||
validate_fixture "$WORK_DIR/valid.json"
|
||||
|
||||
cat >"$WORK_DIR/bad-direct-action.json" <<'JSON'
|
||||
{
|
||||
"settings": { ".name": "settings", ".type": "settings" },
|
||||
"section": [
|
||||
{ ".name": "old", ".type": "section", "enabled": "1", "action": "direct" }
|
||||
]
|
||||
}
|
||||
JSON
|
||||
assert_rejects "bad direct action" "$WORK_DIR/bad-direct-action.json" "unsupported action 'direct'"
|
||||
|
||||
cat >"$WORK_DIR/bad-port.json" <<'JSON'
|
||||
{
|
||||
"settings": { ".name": "settings", ".type": "settings" },
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
reject_runtime_pattern() {
|
||||
local pattern="$1"
|
||||
local label="$2"
|
||||
|
||||
if rg -n --fixed-strings "$pattern" "$ROOT_DIR/podkop/files" >/dev/null 2>&1; then
|
||||
rg -n --fixed-strings "$pattern" "$ROOT_DIR/podkop/files" >&2
|
||||
fail "$label"
|
||||
fi
|
||||
}
|
||||
|
||||
reject_runtime_regex() {
|
||||
local pattern="$1"
|
||||
local label="$2"
|
||||
|
||||
if rg -n "$pattern" "$ROOT_DIR/podkop/files" >/dev/null 2>&1; then
|
||||
rg -n "$pattern" "$ROOT_DIR/podkop/files" >&2
|
||||
fail "$label"
|
||||
fi
|
||||
}
|
||||
|
||||
reject_runtime_pattern "ucode sing-box runtime generator" \
|
||||
"sing-box logs must not expose the implementation language"
|
||||
reject_runtime_pattern "runtime generator" \
|
||||
"sing-box generator errors must describe config generation, not the internal generator"
|
||||
reject_runtime_pattern "Reload signature changes:" \
|
||||
"reload logs must not dump internal signature vectors"
|
||||
reject_runtime_pattern "Reload plan: sing-box=" \
|
||||
"reload logs must not dump boolean plan internals"
|
||||
reject_runtime_pattern "Trying subscription User-Agent" \
|
||||
"subscription logs must not expose automatic User-Agent fallback attempts"
|
||||
reject_runtime_pattern "Selected subscription User-Agent" \
|
||||
"subscription logs must not expose automatic User-Agent fallback selection"
|
||||
reject_runtime_pattern "Current sing-box config hash:" \
|
||||
"sing-box config logs must not emit duplicate hash lines"
|
||||
reject_runtime_pattern "Temporary sing-box config hash:" \
|
||||
"sing-box config logs must not emit duplicate hash lines"
|
||||
reject_runtime_pattern "Podkop Plus did not reach a stable running state after start" \
|
||||
"startup rollback logs must not be fatal secondary errors"
|
||||
reject_runtime_pattern "Flush nft" \
|
||||
"stop logs must not expose routine nft cleanup as user-facing info"
|
||||
reject_runtime_pattern "Flush ip rule" \
|
||||
"stop logs must not expose routine ip-rule cleanup as user-facing info"
|
||||
reject_runtime_pattern "Flush ip route" \
|
||||
"stop logs must not expose routine route cleanup as user-facing info"
|
||||
reject_runtime_pattern "Create IPv4 marking rule" \
|
||||
"nft logs must use polished wording"
|
||||
reject_runtime_pattern "Create IPv6 marking rule" \
|
||||
"nft logs must use polished wording"
|
||||
reject_runtime_pattern "marking rule exist" \
|
||||
"nft logs must use correct grammar"
|
||||
reject_runtime_pattern "br_netfilter enabled detected. Disabling" \
|
||||
"nft logs must use polished bridge-netfilter wording"
|
||||
|
||||
reject_runtime_regex 'log_message\("subscription/cache\.uc: ' \
|
||||
"runtime logs must not expose subscription/cache.uc as a user-facing prefix"
|
||||
reject_runtime_regex 'log_message\("singbox/runtime\.uc: ' \
|
||||
"runtime logs must not expose singbox/runtime.uc as a user-facing prefix"
|
||||
|
||||
printf 'log hygiene regression checks passed\n'
|
||||
@@ -44,6 +44,21 @@ assert_eq() {
|
||||
[ "$actual" = "$expected" ] || fail "$label: expected '$expected', got '$actual'"
|
||||
}
|
||||
|
||||
assert_line_before() {
|
||||
local file="$1"
|
||||
local first="$2"
|
||||
local second="$3"
|
||||
local label="$4"
|
||||
local first_line
|
||||
local second_line
|
||||
|
||||
first_line="$(grep -Fn "$first" "$file" | head -n 1 | cut -d: -f1 || true)"
|
||||
second_line="$(grep -Fn "$second" "$file" | head -n 1 | cut -d: -f1 || true)"
|
||||
[ -n "$first_line" ] || fail "$label: missing first line '$first'"
|
||||
[ -n "$second_line" ] || fail "$label: missing second line '$second'"
|
||||
[ "$first_line" -lt "$second_line" ] || fail "$label: expected first line before second"
|
||||
}
|
||||
|
||||
if grep -n 'require("uci").cursor' "$NFT_RUNTIME" >/dev/null 2>&1 ||
|
||||
grep -n 'uci -q' "$NFT_RUNTIME" >/dev/null 2>&1 ||
|
||||
grep -n '"uci", "-q"' "$NFT_RUNTIME" >/dev/null 2>&1 ||
|
||||
@@ -189,11 +204,15 @@ assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tPodkopPlusTable\tpodkop_plus_i
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_interfaces\t{ br-lan }' "runtime br-lan interface"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_interfaces\t{ tun0 }' "runtime tun0 interface"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tchain\tinet\tPodkopPlusTable\tmangle\t{ type filter hook prerouting priority -150; policy accept; }' "runtime mangle chain"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tchain\tinet\tPodkopPlusTable\tpriority_rules\t{ }' "runtime priority chain"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tchain\tinet\tPodkopPlusTable\tpriority_output_rules\t{ }' "runtime priority output chain"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle\tjump\tpriority_rules' "runtime priority jump"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle\tiifname\t@podkop_plus_interfaces\tip\tdaddr\t@podkop_plus_subnets\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime common tcp rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle\tiifname\t@podkop_plus_interfaces\tip6\tdaddr\t@podkop_plus_subnets6\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime common6 tcp rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tproxy\tmeta\tmark\t&\t0x00100000\t==\t0x00100000\tmeta\tl4proto\ttcp\ttproxy\tip\tto\t:1602\tcounter' "runtime proxy tcp rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tproxy\tmeta\tmark\t&\t0x00100000\t==\t0x00100000\tmeta\tl4proto\ttcp\ttproxy\tip6\tto\t[::1]:1602\tcounter' "runtime proxy6 tcp rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tmeta\tmark\t0x00200000\tcounter\treturn' "runtime outbound return"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tjump\tpriority_output_rules' "runtime priority output jump"
|
||||
assert_contains "$NFT_LOG" $'nft\tinsert\trule\tinet\tPodkopPlusTable\tmangle\tudp\tdport\t123\treturn' "runtime ntp exclusion"
|
||||
|
||||
cat >"$WORK_DIR/runtime-base-uci.state" <<'EOF_UCI'
|
||||
@@ -228,7 +247,7 @@ assert_contains "$NFT_LOG" $'nft\tlist\ttable\tinet\tPodkopPlusTable' "nft table
|
||||
cat >"$WORK_DIR/provider-rules.json" <<'JSON'
|
||||
{
|
||||
"section": [
|
||||
{ ".name": "direct", "enabled": "1", "action": "direct" },
|
||||
{ ".name": "direct", "enabled": "1", "action": "bypass" },
|
||||
{ ".name": "zapret_one", "enabled": "1", "action": "zapret" },
|
||||
{ ".name": "zapret_disabled", "enabled": "0", "action": "zapret" },
|
||||
{ ".name": "zapret_two", "enabled": "1", "action": "zapret" },
|
||||
@@ -255,6 +274,46 @@ if [ -s "$NFT_LOG" ]; then
|
||||
fail "missing provider binary should skip provider nft output rules"
|
||||
fi
|
||||
|
||||
cat >"$WORK_DIR/priority-rules.json" <<'JSON'
|
||||
{
|
||||
"section": [
|
||||
{
|
||||
".name": "bypass_first",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "bypass",
|
||||
"ip_cidr": [ "198.51.100.0/24" ]
|
||||
},
|
||||
{
|
||||
".name": "wide_proxy",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "proxy",
|
||||
"ip_cidr": [ "0.0.0.0/0" ]
|
||||
},
|
||||
{
|
||||
".name": "port_bypass",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "bypass",
|
||||
"ports": [ "8443" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
|
||||
: > "$NFT_LOG"
|
||||
nft_ucode nft-add-section-priority-rules-fixture "$WORK_DIR/priority-rules.json" PodkopPlusTable podkop_plus_interfaces localv4 localv6 0x00100000
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tPodkopPlusTable\tpodkop_plus_rule_bypass_first_subnets\t{ type ipv4_addr; flags interval; auto-merge; }' "bypass priority subnet set"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tPodkopPlusTable\tpodkop_plus_rule_wide_proxy_subnets\t{ type ipv4_addr; flags interval; auto-merge; }' "proxy priority subnet set"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tpriority_rules\tiifname\t@podkop_plus_interfaces\tip\tdaddr\t!=\t@localv4\tip\tdaddr\t@podkop_plus_rule_bypass_first_subnets\tcounter\taccept' "bypass priority accept rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tpriority_rules\tiifname\t@podkop_plus_interfaces\tip\tdaddr\t!=\t@localv4\tip\tdaddr\t@podkop_plus_rule_wide_proxy_subnets\tmeta\tmark\tset\t0x00100000\tcounter\taccept' "proxy priority capture rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tpriority_rules\tiifname\t@podkop_plus_interfaces\tip\tdaddr\t!=\t@localv4\ttcp\tdport\t@podkop_plus_rule_port_bypass_ports\tcounter\taccept' "port-only bypass priority rule"
|
||||
assert_line_before "$NFT_LOG" \
|
||||
$'nft\tadd\trule\tinet\tPodkopPlusTable\tpriority_rules\tiifname\t@podkop_plus_interfaces\tip\tdaddr\t!=\t@localv4\tip\tdaddr\t@podkop_plus_rule_bypass_first_subnets\tcounter\taccept' \
|
||||
$'nft\tadd\trule\tinet\tPodkopPlusTable\tpriority_rules\tiifname\t@podkop_plus_interfaces\tip\tdaddr\t!=\t@localv4\tip\tdaddr\t@podkop_plus_rule_wide_proxy_subnets\tmeta\tmark\tset\t0x00100000\tcounter\taccept' \
|
||||
"bypass priority order"
|
||||
|
||||
: > "$IP_LOG"
|
||||
: > "$LOGGER_LOG"
|
||||
rt_tables="$WORK_DIR/rt_tables"
|
||||
@@ -264,10 +323,10 @@ assert_contains "$IP_LOG" $'ip\troute\tadd\tlocal\t0.0.0.0/0\tdev\tlo\ttable\tpo
|
||||
assert_contains "$IP_LOG" $'ip\t-6\troute\tadd\tlocal\t::/0\tdev\tlo\ttable\tpodkop' "tproxy route6 add"
|
||||
assert_contains "$IP_LOG" $'ip\t-4\trule\tadd\tfwmark\t0x00100000/0x00100000\ttable\tpodkop\tpriority\t105' "tproxy marking rule add"
|
||||
assert_contains "$IP_LOG" $'ip\t-6\trule\tadd\tfwmark\t0x00100000/0x00100000\ttable\tpodkop\tpriority\t105' "tproxy marking rule6 add"
|
||||
assert_contains "$LOGGER_LOG" "[debug] Added IPv4 route for tproxy" "tproxy route creation log"
|
||||
assert_contains "$LOGGER_LOG" "[debug] Added IPv6 route for tproxy" "tproxy route6 creation log"
|
||||
assert_contains "$LOGGER_LOG" "[debug] Create IPv4 marking rule" "tproxy marking rule creation log"
|
||||
assert_contains "$LOGGER_LOG" "[debug] Create IPv6 marking rule" "tproxy marking rule6 creation log"
|
||||
assert_contains "$LOGGER_LOG" "[debug] Added IPv4 TPROXY route" "tproxy route creation log"
|
||||
assert_contains "$LOGGER_LOG" "[debug] Added IPv6 TPROXY route" "tproxy route6 creation log"
|
||||
assert_contains "$LOGGER_LOG" "[debug] Creating IPv4 TPROXY marking rule" "tproxy marking rule creation log"
|
||||
assert_contains "$LOGGER_LOG" "[debug] Creating IPv6 TPROXY marking rule" "tproxy marking rule6 creation log"
|
||||
|
||||
: > "$IP_LOG"
|
||||
: > "$LOGGER_LOG"
|
||||
@@ -280,10 +339,10 @@ IP_ROUTE_OUTPUT='local default dev lo scope host' \
|
||||
if grep -Fq $'\tadd\t' "$IP_LOG"; then
|
||||
fail "existing tproxy route/rule should not be added again"
|
||||
fi
|
||||
assert_contains "$LOGGER_LOG" "[debug] IPv4 route for tproxy exists" "existing tproxy route log"
|
||||
assert_contains "$LOGGER_LOG" "[debug] IPv6 route for tproxy exists" "existing tproxy route6 log"
|
||||
assert_contains "$LOGGER_LOG" "[debug] IPv4 marking rule exist" "existing tproxy marking rule log"
|
||||
assert_contains "$LOGGER_LOG" "[debug] IPv6 marking rule exist" "existing tproxy marking rule6 log"
|
||||
assert_contains "$LOGGER_LOG" "[debug] IPv4 TPROXY route already exists" "existing tproxy route log"
|
||||
assert_contains "$LOGGER_LOG" "[debug] IPv6 TPROXY route already exists" "existing tproxy route6 log"
|
||||
assert_contains "$LOGGER_LOG" "[debug] IPv4 TPROXY marking rule already exists" "existing tproxy marking rule log"
|
||||
assert_contains "$LOGGER_LOG" "[debug] IPv6 TPROXY marking rule already exists" "existing tproxy marking rule6 log"
|
||||
IP_ROUTE_OUTPUT='local default dev lo scope host' \
|
||||
IP_ROUTE6_OUTPUT='local default dev lo metric 1024 pref medium' \
|
||||
IP_RULE_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup podkop' \
|
||||
@@ -303,7 +362,7 @@ LSMOD_OUTPUT='br_netfilter 32768 0' SYSCTL_BRIDGE_NF_CALL_IPTABLES=1 nft_ucode e
|
||||
assert_contains "$SYSCTL_LOG" $'sysctl\t-n\tnet.bridge.bridge-nf-call-iptables' "bridge netfilter sysctl check"
|
||||
assert_contains "$SYSCTL_LOG" $'sysctl\t-w\tnet.bridge.bridge-nf-call-iptables=0' "bridge netfilter ipv4 disable"
|
||||
assert_contains "$SYSCTL_LOG" $'sysctl\t-w\tnet.bridge.bridge-nf-call-ip6tables=0' "bridge netfilter ipv6 disable"
|
||||
assert_contains "$LOGGER_LOG" "[debug] br_netfilter enabled detected. Disabling" "bridge netfilter disable log"
|
||||
assert_contains "$LOGGER_LOG" "[debug] br_netfilter is enabled; disabling it for transparent proxy routing" "bridge netfilter disable log"
|
||||
|
||||
: > "$SYSCTL_LOG"
|
||||
LSMOD_OUTPUT='' SYSCTL_BRIDGE_NF_CALL_IPTABLES=1 nft_ucode ensure-bridge-netfilter-disabled
|
||||
@@ -341,6 +400,7 @@ cat >"$WORK_DIR/populate-fixture.json" <<'JSON'
|
||||
".name": "disabled",
|
||||
".type": "section",
|
||||
"enabled": "0",
|
||||
"action": "proxy",
|
||||
"ip_cidr": [ "192.0.2.1" ],
|
||||
"ports": [ "53" ],
|
||||
"fully_routed_ips": [ "192.168.1.50/32" ]
|
||||
@@ -349,6 +409,7 @@ cat >"$WORK_DIR/populate-fixture.json" <<'JSON'
|
||||
".name": "deferred",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "proxy",
|
||||
"ip_cidr": [ "192.0.2.2" ],
|
||||
"ports": [ "54" ],
|
||||
"fully_routed_ips": [ "192.168.1.51/32" ]
|
||||
@@ -357,6 +418,7 @@ cat >"$WORK_DIR/populate-fixture.json" <<'JSON'
|
||||
".name": "inline",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "proxy",
|
||||
"ip_cidr": [ "198.51.100.1", "203.0.113.0/24", "2001:db8::1" ],
|
||||
"ports": [ "80", "443-444" ],
|
||||
"fully_routed_ips": [ "192.168.1.20/32", "192.168.1.20/32", "2001:db8::20/128" ]
|
||||
@@ -365,12 +427,14 @@ cat >"$WORK_DIR/populate-fixture.json" <<'JSON'
|
||||
".name": "inline_no_ports",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "bypass",
|
||||
"ip_cidr": [ "198.51.100.200", "2001:db8::200" ]
|
||||
},
|
||||
{
|
||||
".name": "ports_only",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "proxy",
|
||||
"ports": [ "53", "853" ],
|
||||
"ports_text": "5353, 853"
|
||||
},
|
||||
@@ -378,6 +442,7 @@ cat >"$WORK_DIR/populate-fixture.json" <<'JSON'
|
||||
".name": "ports_with_domain",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "proxy",
|
||||
"domain_suffix": [ "example.org" ],
|
||||
"ports": [ "8443" ]
|
||||
}
|
||||
@@ -394,8 +459,8 @@ EOF_INPUT
|
||||
|
||||
: > "$NFT_LOG"
|
||||
nft_ucode nft-add-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" inline_no_ports "$plain_subnets" PodkopPlusTable podkop_plus_subnets podkop_plus_ip_ports 5000
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_subnets\t{ 198.51.100.210,203.0.113.0/24 }' "plain subnet import without ports"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_subnets6\t{ 2001:db8::210 }' "plain subnet6 import without ports"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_subnets\t{ 198.51.100.210,203.0.113.0/24 }' "plain subnet import without ports"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_subnets6\t{ 2001:db8::210 }' "plain subnet6 import without ports"
|
||||
|
||||
: > "$NFT_LOG"
|
||||
nft_ucode nft-add-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" ports_only "$plain_subnets" PodkopPlusTable podkop_plus_subnets podkop_plus_ip_ports 3
|
||||
@@ -426,9 +491,9 @@ JSON
|
||||
unscoped_json="$WORK_DIR/unscoped-json.txt"
|
||||
scoped_json="$WORK_DIR/scoped-json.txt"
|
||||
nft_ucode nft-add-json-ruleset-subnets-for-section-fixture "$WORK_DIR/populate-fixture.json" inline_no_ports "$json_ruleset" "fixture json" PodkopPlusTable podkop_plus_subnets podkop_plus_ip_ports "$unscoped_json" "$scoped_json" 5000
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_subnets\t{ 198.51.100.220 }' "json ruleset unscoped import"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_subnets6\t{ 2001:db8::220 }' "json ruleset unscoped6 import"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_ip_ports\t{ 198.51.100.221 . 443,198.51.100.222 . 1000-1002 }' "json ruleset own port filters"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_subnets\t{ 198.51.100.220 }' "json ruleset unscoped import"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_subnets6\t{ 2001:db8::220 }' "json ruleset unscoped6 import"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_ip_ports\t{ 198.51.100.221 . 443,198.51.100.222 . 1000-1002 }' "json ruleset own port filters"
|
||||
|
||||
: > "$NFT_LOG"
|
||||
unscoped_json="$WORK_DIR/unscoped-json-ports.txt"
|
||||
@@ -441,16 +506,11 @@ fi
|
||||
|
||||
: > "$NFT_LOG"
|
||||
nft_ucode nft-add-community-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" inline_no_ports discord "$plain_subnets" PodkopPlusTable podkop_plus_subnets podkop_plus_ip_ports podkop_plus_interfaces podkop_plus_discord_subnets 0x00100000 5000
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tPodkopPlusTable\tpodkop_plus_discord_subnets\t{ type ipv4_addr; flags interval; auto-merge; }' "discord community set"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tPodkopPlusTable\tpodkop_plus_discord_subnets6\t{ type ipv6_addr; flags interval; auto-merge; }' "discord community6 set"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle\tiifname\t@podkop_plus_interfaces\tip\tdaddr\t@podkop_plus_discord_subnets\tudp\tdport\t{ 19000-20000, 50000-65535 }\tmeta\tmark\tset\t0x00100000\tcounter' "discord community udp rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle\tiifname\t@podkop_plus_interfaces\tip6\tdaddr\t@podkop_plus_discord_subnets6\tudp\tdport\t{ 19000-20000, 50000-65535 }\tmeta\tmark\tset\t0x00100000\tcounter' "discord community udp6 rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_discord_subnets\t{ 198.51.100.210,203.0.113.0/24 }' "discord community subnet import"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_subnets\t{ 198.51.100.210,203.0.113.0/24 }' "discord community subnet import"
|
||||
|
||||
: > "$NFT_LOG"
|
||||
NFT_MANGLE_CHAIN_OUTPUT=$'iifname @podkop_plus_interfaces ip daddr @podkop_plus_discord_subnets udp dport { 19000-20000, 50000-65535 } meta mark set 0x00100000 counter\niifname @podkop_plus_interfaces ip6 daddr @podkop_plus_discord_subnets6 udp dport { 19000-20000, 50000-65535 } meta mark set 0x00100000 counter' \
|
||||
nft_ucode nft-add-community-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" inline_no_ports discord "$plain_subnets" PodkopPlusTable podkop_plus_subnets podkop_plus_ip_ports podkop_plus_interfaces podkop_plus_discord_subnets 0x00100000 5000
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tPodkopPlusTable\tpodkop_plus_discord_subnets\t{ type ipv4_addr; flags interval; auto-merge; }' "discord community existing set"
|
||||
if grep -Fq $'nft\tadd\trule' "$NFT_LOG"; then
|
||||
fail "discord community existing rule should not insert"
|
||||
fi
|
||||
@@ -466,9 +526,11 @@ assert_contains "$NFT_LOG" $'198.51.100.210 . 53,198.51.100.210 . 853,198.51.100
|
||||
nft_ucode nft-populate-runtime-sets-fixture "$WORK_DIR/populate-fixture.json" 1 "deferred" PodkopPlusTable podkop_plus_subnets podkop_plus_ports podkop_plus_ip_ports podkop_plus_interfaces localv4 0x00100000
|
||||
assert_contains "$NFT_LOG" $'198.51.100.1 . 80,198.51.100.1 . 443-444,203.0.113.0/24 . 80' "populate inline ip-port first chunk"
|
||||
assert_contains "$NFT_LOG" $'203.0.113.0/24 . 443-444' "populate inline ip-port second chunk"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_subnets\t{ 198.51.100.200 }' "populate inline ip without ports"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_subnets6\t{ 2001:db8::200 }' "populate inline ip6 without ports"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_ports\t{ 53,853,5353 }' "populate ports-only set"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_ip_ports\t{ 198.51.100.1 . 80,198.51.100.1 . 443-444,203.0.113.0/24 . 80,203.0.113.0/24 . 443-444 }' "populate inline priority ip-port set"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_ip6_ports\t{ 2001:db8::1 . 80,2001:db8::1 . 443-444 }' "populate inline priority ip6-port set"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_subnets\t{ 198.51.100.200 }' "populate inline ip without ports"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_subnets6\t{ 2001:db8::200 }' "populate inline ip6 without ports"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_ports_only_ports\t{ 53,853,5353 }' "populate ports-only set"
|
||||
assert_contains "$NFT_LOG" $'nft\tinsert\trule\tinet\tPodkopPlusTable\tmangle\tiifname\t@podkop_plus_interfaces\tip\tsaddr\t192.168.1.20/32\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "populate fully routed tcp"
|
||||
assert_contains "$NFT_LOG" $'nft\tinsert\trule\tinet\tPodkopPlusTable\tmangle\tiifname\t@podkop_plus_interfaces\tip6\tsaddr\t2001:db8::20/128\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "populate fully routed6 tcp"
|
||||
if grep -Fq '192.0.2.1' "$NFT_LOG"; then
|
||||
@@ -477,7 +539,7 @@ fi
|
||||
if grep -Fq '192.0.2.2' "$NFT_LOG"; then
|
||||
fail "deferred section should not populate nft"
|
||||
fi
|
||||
if grep -Fq $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_ports\t{ 8443 }' "$NFT_LOG"; then
|
||||
if grep -Fq $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_ports_with_domain_ports\t{ 8443 }' "$NFT_LOG"; then
|
||||
fail "ports with destination matchers should not populate global port set"
|
||||
fi
|
||||
if [ "$(grep -F $'ip\tsaddr\t192.168.1.20/32' "$NFT_LOG" | wc -l | tr -d ' ')" != "3" ]; then
|
||||
@@ -495,6 +557,7 @@ cat >"$WORK_DIR/signature-fixture.json" <<'JSON'
|
||||
".name": "disabled",
|
||||
".type": "section",
|
||||
"enabled": "0",
|
||||
"action": "proxy",
|
||||
"ip_cidr": [ "192.0.2.10" ],
|
||||
"ports": [ "1234" ],
|
||||
"community_lists": [ "discord" ]
|
||||
@@ -503,6 +566,7 @@ cat >"$WORK_DIR/signature-fixture.json" <<'JSON'
|
||||
".name": "text_rule",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "bypass",
|
||||
"ip_cidr_text_mode": "1",
|
||||
"ip_cidr_text": "198.51.100.1\nbad-value\n203.0.113.0/24",
|
||||
"ip_cidr": [ "10.0.0.0/8" ],
|
||||
@@ -517,6 +581,7 @@ cat >"$WORK_DIR/signature-fixture.json" <<'JSON'
|
||||
{
|
||||
".name": "default_enabled",
|
||||
".type": "section",
|
||||
"action": "proxy",
|
||||
"ip_cidr": [ "10.0.0.0/8", "192.0.2.1" ],
|
||||
"ports_text": "53 853",
|
||||
"community_lists": [ "youtube", "cloudflare", "roblox" ]
|
||||
@@ -530,8 +595,12 @@ cat >"$WORK_DIR/signature-expected.txt" <<'EOF_EXPECTED'
|
||||
br-lan tun0
|
||||
[settings.exclude_ntp]
|
||||
1
|
||||
[rule.text_rule.action]
|
||||
bypass
|
||||
[rule.text_rule.ip_cidr]
|
||||
198.51.100.1,203.0.113.0/24
|
||||
[rule.text_rule.source_ip_cidr]
|
||||
|
||||
[rule.text_rule.ports]
|
||||
443,80,443-444
|
||||
[rule.text_rule.fully_routed_ips]
|
||||
@@ -544,8 +613,12 @@ https://example.com/subnets.lst
|
||||
/tmp/local.json
|
||||
[rule.text_rule.domain_ip_lists]
|
||||
https://example.com/mixed.lst
|
||||
[rule.default_enabled.action]
|
||||
proxy
|
||||
[rule.default_enabled.ip_cidr]
|
||||
10.0.0.0/8,192.0.2.1
|
||||
[rule.default_enabled.source_ip_cidr]
|
||||
|
||||
[rule.default_enabled.ports]
|
||||
53,853
|
||||
[rule.default_enabled.fully_routed_ips]
|
||||
@@ -573,6 +646,7 @@ podkop-plus.disabled.enabled=0
|
||||
podkop-plus.disabled.ip_cidr=192.0.2.10
|
||||
podkop-plus.enabled=section
|
||||
podkop-plus.enabled.enabled=1
|
||||
podkop-plus.enabled.action=bypass
|
||||
podkop-plus.enabled.ip_cidr=10.0.0.0/8 192.0.2.1
|
||||
podkop-plus.enabled.ports=443 bad-value
|
||||
podkop-plus.enabled.ports_text=80 443-444 65536
|
||||
@@ -587,8 +661,12 @@ cat >"$WORK_DIR/signature-uci-expected.txt" <<'EOF_EXPECTED'
|
||||
br-lan tun0
|
||||
[settings.exclude_ntp]
|
||||
1
|
||||
[rule.enabled.action]
|
||||
bypass
|
||||
[rule.enabled.ip_cidr]
|
||||
10.0.0.0/8,192.0.2.1
|
||||
[rule.enabled.source_ip_cidr]
|
||||
|
||||
[rule.enabled.ports]
|
||||
443,80,443-444
|
||||
[rule.enabled.fully_routed_ips]
|
||||
|
||||
@@ -38,7 +38,7 @@ fi
|
||||
assert_eq "2" "$(rows | rules_ucode count zapret)" "enabled zapret count"
|
||||
assert_eq "1" "$(rows | rules_ucode count byedpi)" "enabled byedpi count"
|
||||
assert_eq "1" "$(rows | rules_ucode count zapret2)" "enabled zapret2 count"
|
||||
assert_eq "0" "$(rows | rules_ucode count direct)" "missing action count"
|
||||
assert_eq "0" "$(rows | rules_ucode count bypass)" "non-provider action count"
|
||||
|
||||
assert_eq "1" "$(rows | rules_ucode index zapret alpha)" "first zapret index"
|
||||
assert_eq "2" "$(rows | rules_ucode index zapret delta)" "second zapret index skips disabled rows"
|
||||
|
||||
@@ -264,7 +264,6 @@ cat >"$WORK_DIR/sing-box-signature.json" <<'JSON'
|
||||
"dns_rewrite_ttl": "120",
|
||||
"output_network_interface": "wan",
|
||||
"disable_quic": "on",
|
||||
"routing_excluded_ips": [ "192.0.2.0/24" ],
|
||||
"list_update_enabled": "1",
|
||||
"update_interval": "2h",
|
||||
"cache_path": "/tmp/cache.db",
|
||||
@@ -392,8 +391,6 @@ tcp
|
||||
wan
|
||||
[settings.disable_quic]
|
||||
1
|
||||
[settings.routing_excluded_ips]
|
||||
192.0.2.0/24
|
||||
[settings.update_interval]
|
||||
2h
|
||||
[settings.cache_path]
|
||||
|
||||
@@ -211,8 +211,7 @@ cat >"$WORK_DIR/runtime-matchers-fixture.json" <<'JSON'
|
||||
".type": "settings",
|
||||
"config_path": "/tmp/sing-box/config.json",
|
||||
"dns_server": "1.1.1.1",
|
||||
"service_listen_address": "127.0.0.1",
|
||||
"routing_excluded_ips": [ "192.168.1.5/32", "2001:db8::5/128" ]
|
||||
"service_listen_address": "127.0.0.1"
|
||||
},
|
||||
"section": [
|
||||
{
|
||||
@@ -223,13 +222,22 @@ cat >"$WORK_DIR/runtime-matchers-fixture.json" <<'JSON'
|
||||
"outbound_json": "{\"type\":\"direct\"}",
|
||||
"domain_suffix": [ "detour.example" ]
|
||||
},
|
||||
{
|
||||
".name": "bypass",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "bypass",
|
||||
"domain_suffix": [ "example.org" ],
|
||||
"ip_cidr": [ "198.51.100.0/24" ],
|
||||
"source_ip_cidr": [ "10.0.0.3/32" ]
|
||||
},
|
||||
{
|
||||
".name": "proxy",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "outbound",
|
||||
"outbound_json": "{\"type\":\"socks\",\"server\":\"127.0.0.1\",\"server_port\":1080}",
|
||||
"domain_suffix": [ "example.org" ],
|
||||
"domain_suffix": [ "proxy.example.org" ],
|
||||
"source_ip_cidr": [ "10.0.0.2/32", "2001:db8::2/128" ],
|
||||
"outbound_detour_enabled": "1",
|
||||
"outbound_detour_section": "detour",
|
||||
@@ -645,12 +653,16 @@ assert(matchers.dns.strategy == "prefer_ipv4", "DNS strategy allows IPv6 answers
|
||||
assert(dns_server(matchers, r => r.tag == "fakeip-server" && r.inet6_range == "fc00::/18") != null, "FakeIP IPv6 range");
|
||||
assert(inbound(matchers, "tproxy6-in") != null, "IPv6 TProxy inbound");
|
||||
assert(inbound(matchers, "tproxy6-in").listen == "::1", "IPv6 TProxy listen address");
|
||||
assert(outbound(matchers, "bypass-out").type == "direct", "bypass fallback outbound");
|
||||
assert(outbound(matchers, "proxy-out").detour == "detour-out", "outbound detour");
|
||||
let mixed = inbound(matchers, "proxy-mixed-in");
|
||||
assert(mixed && mixed.listen_port == 19090 && mixed.users[0].username == "user", "mixed inbound auth");
|
||||
assert(route_rule(matchers, r => r.inbound == "proxy-mixed-in" && r.outbound == "proxy-out") != null, "mixed inbound route");
|
||||
assert(dns_rule(matchers, r => contains(r.domain_suffix, "example.org")).server == "dns-server", "bypass domain real DNS rule");
|
||||
assert(dns_rule(matchers, r => contains(r.domain_suffix, "proxy.example.org")).server == "fakeip-server", "proxy domain FakeIP DNS rule");
|
||||
assert(route_rule(matchers, r => r.outbound == "bypass-out" && contains(r.domain_suffix, "example.org") && contains(r.source_ip_cidr, "10.0.0.3/32")) != null, "bypass fallback route");
|
||||
assert(route_rule(matchers, r => contains(r.inbound, "tproxy-in") && contains(r.inbound, "tproxy6-in") && r.outbound == "proxy-out") != null, "section route dual tproxy inbound");
|
||||
assert(route_rule(matchers, r => r.outbound == "direct-out" && contains(r.source_ip_cidr, "192.168.1.5/32") && contains(r.source_ip_cidr, "2001:db8::5/128")) != null, "routing excluded source");
|
||||
assert(route_rule(matchers, r => r.outbound == "direct-out" && contains(r.source_ip_cidr, "192.168.1.5/32")) == null, "routing excluded source removed");
|
||||
assert(route_rule(matchers, r => r.outbound == "proxy-out" && contains(r.source_ip_cidr, "10.0.0.2/32") && contains(r.source_ip_cidr, "2001:db8::2/128")) != null, "source_ip_cidr matcher");
|
||||
|
||||
let urltest = cfg("urltest");
|
||||
|
||||
@@ -104,6 +104,27 @@ if cache_ucode subscription-metadata-path '../bad' >/dev/null 2>&1; then
|
||||
fail "subscription metadata path mode should reject unsafe section names"
|
||||
fi
|
||||
|
||||
cat >"$WORK_DIR/normalized-subscription.json" <<'JSON'
|
||||
{
|
||||
"version": 1,
|
||||
"format": "uri-list",
|
||||
"skipped": 2,
|
||||
"outbounds": [
|
||||
{ "type": "vless", "tag": "one" },
|
||||
{ "type": "trojan", "tag": "two" }
|
||||
]
|
||||
}
|
||||
JSON
|
||||
assert_eq "2 proxy entries, 2 skipped entries" \
|
||||
"$(cache_ucode subscription-import-stats "$WORK_DIR/normalized-subscription.json")" \
|
||||
"subscription import stats"
|
||||
assert_eq "Subscription source 1 for rule 'proxy' imported: 2 proxy entries, 2 skipped entries" \
|
||||
"$(cache_ucode subscription-source-summary proxy 1 "$WORK_DIR/normalized-subscription.json" imported)" \
|
||||
"subscription source imported summary"
|
||||
assert_eq "Subscription source 1 for rule 'proxy' is unchanged: 2 proxy entries, 2 skipped entries" \
|
||||
"$(cache_ucode subscription-source-summary proxy 1 "$WORK_DIR/normalized-subscription.json" unchanged)" \
|
||||
"subscription source unchanged summary"
|
||||
|
||||
runtime_env() {
|
||||
TMP_SING_BOX_FOLDER="$WORK_DIR/runtime/tmp-sing-box" \
|
||||
TMP_RULESET_FOLDER="$WORK_DIR/runtime/tmp-sing-box/rulesets" \
|
||||
@@ -172,7 +193,7 @@ cat >"$WORK_DIR/cache-maintenance.json" <<'JSON'
|
||||
{
|
||||
".name": "direct",
|
||||
"enabled": "1",
|
||||
"action": "direct"
|
||||
"action": "bypass"
|
||||
},
|
||||
{
|
||||
".name": "disabled",
|
||||
|
||||
@@ -103,7 +103,7 @@ if ui_ucode service-action-expected-running invalid >/dev/null 2>&1; then
|
||||
fi
|
||||
ui_ucode latency-type-valid proxy_list >/dev/null ||
|
||||
fail "proxy_list latency type should be valid"
|
||||
if ui_ucode latency-type-valid direct >/dev/null 2>&1; then
|
||||
if ui_ucode latency-type-valid invalid >/dev/null 2>&1; then
|
||||
fail "invalid latency type should be rejected"
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user