diff --git a/fe-app-tachyon/locales/tachyon.ru.po b/fe-app-tachyon/locales/tachyon.ru.po index 3afc407b..794aa8e0 100644 --- a/fe-app-tachyon/locales/tachyon.ru.po +++ b/fe-app-tachyon/locales/tachyon.ru.po @@ -6742,3 +6742,15 @@ msgstr "Блокировать: " msgid "Traffic from these IP addresses will never be routed through this section, even if other conditions match." msgstr "Трафик от этих IP-адресов никогда не будет маршрутизирован через эту секцию, даже если другие условия совпадают." + +msgid "Route Router's Own Traffic" +msgstr "Маршрутизировать собственный трафик роутера" + +msgid "Route local traffic originating directly from the router (e.g. wget, curl, opkg, torrent clients on the router) through Tachyon." +msgstr "Направлять локальный трафик, исходящий непосредственно с роутера (например, wget, curl, opkg, торрент-клиент на роутере), через Tachyon." + +msgid "Router Traffic Target Section" +msgstr "Секция для трафика роутера" + +msgid "Select the section to route router's own traffic through." +msgstr "Выберите секцию для маршрутизации трафика роутера." diff --git a/luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/settings.js b/luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/settings.js index d65bfe4e..5f2612ff 100644 --- a/luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/settings.js +++ b/luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/settings.js @@ -1806,6 +1806,31 @@ function createSettingsContent(section, capabilities) { return true; }; + o = section.taboption( + "network", + form.Flag, + "route_router_traffic", + _("Route Router's Own Traffic"), + _( + "Route local traffic originating directly from the router (e.g. wget, curl, opkg, torrent clients on the router) through Tachyon.", + ), + ); + configureDownloadViaProxyFlag(o, "route_router_traffic_section"); + + o = section.taboption( + "network", + form.ListValue, + "route_router_traffic_section", + _("Router Traffic Target Section"), + _("Select the section to route router's own traffic through."), + ); + o.depends("route_router_traffic", "1"); + configureDownloadSectionOption( + o, + "route_router_traffic_section", + capabilities, + ); + o = section.taboption( "services", form.Flag, diff --git a/luci-app-tachyon/po/ru/tachyon.po b/luci-app-tachyon/po/ru/tachyon.po index 3afc407b..794aa8e0 100644 --- a/luci-app-tachyon/po/ru/tachyon.po +++ b/luci-app-tachyon/po/ru/tachyon.po @@ -6742,3 +6742,15 @@ msgstr "Блокировать: " msgid "Traffic from these IP addresses will never be routed through this section, even if other conditions match." msgstr "Трафик от этих IP-адресов никогда не будет маршрутизирован через эту секцию, даже если другие условия совпадают." + +msgid "Route Router's Own Traffic" +msgstr "Маршрутизировать собственный трафик роутера" + +msgid "Route local traffic originating directly from the router (e.g. wget, curl, opkg, torrent clients on the router) through Tachyon." +msgstr "Направлять локальный трафик, исходящий непосредственно с роутера (например, wget, curl, opkg, торрент-клиент на роутере), через Tachyon." + +msgid "Router Traffic Target Section" +msgstr "Секция для трафика роутера" + +msgid "Select the section to route router's own traffic through." +msgstr "Выберите секцию для маршрутизации трафика роутера." diff --git a/tachyon/files/usr/lib/nft/apply.uc b/tachyon/files/usr/lib/nft/apply.uc index c847ad8f..4351786e 100644 --- a/tachyon/files/usr/lib/nft/apply.uc +++ b/tachyon/files/usr/lib/nft/apply.uc @@ -2570,6 +2570,63 @@ function nft_guest_mode_signature_body(body, guest_mode) { return body; } +function router_output_intercept_enabled(settings) { + if (type(settings) != "object") + settings = uci_settings(); + return bool_option(settings, "route_router_traffic", false) && + option(settings, "route_router_traffic_section", "") != ""; +} + +function nft_disable_router_output_intercept(table) { + run_args_quiet([ "nft", "delete", "chain", "inet", as_string(table), "output_redirect" ]); + return true; +} + +function nft_enable_router_output_intercept(table, localv4_set, outbound_mark, exclude_ntp) { + table = as_string(table); + localv4_set = as_string(localv4_set || "localv4"); + outbound_mark = as_string(outbound_mark || runtime_constants.OUTBOUND_MARK); + let port = as_string(runtime_constants.REDIRECT_INBOUND_PORT); + + if (!run_args_quiet([ "nft", "flush", "chain", "inet", table, "output_redirect" ])) { + if (!nft_create_chain(table, "output_redirect", "{ type nat hook output priority -100; policy accept; }")) + return false; + } + + if (!nft_add_rule(table, "output_redirect", [ "ct", "status", "dnat", "return" ])) + return false; + if (!nft_add_rule(table, "output_redirect", [ "meta", "l4proto", "icmp", "return" ])) + return false; + if (!nft_add_rule(table, "output_redirect", [ "ip", "daddr", "@" + localv4_set, "return" ])) + return false; + if (!nft_add_rule(table, "output_redirect", [ "tcp", "dport", "53", "return" ])) + return false; + if (!nft_add_rule(table, "output_redirect", [ "udp", "dport", "53", "return" ])) + return false; + if (arg_bool(exclude_ntp) && !nft_add_rule(table, "output_redirect", [ "udp", "dport", "123", "return" ])) + return false; + if (outbound_mark != "" && !nft_add_rule(table, "output_redirect", [ "meta", "mark", outbound_mark, "return" ])) + return false; + return nft_add_rule(table, "output_redirect", [ + "meta", "l4proto", "tcp", "counter", "redirect", "to", ":" + port + ]); +} + +function nft_sync_router_output_intercept(table, localv4_set, outbound_mark) { + table = as_string(table || "tachyon"); + localv4_set = as_string(localv4_set || "localv4"); + outbound_mark = as_string(outbound_mark || runtime_constants.OUTBOUND_MARK); + let settings = uci_settings(); + if (!router_output_intercept_enabled(settings)) + return nft_disable_router_output_intercept(table); + return nft_enable_router_output_intercept( + table, + localv4_set, + outbound_mark, + option(settings, "exclude_ntp", "0") + ); +} + function nft_runtime_signature_from_settings_and_sections(settings, sections, schedules, profiles, guest_modes) { let body = ""; @@ -2580,6 +2637,8 @@ function nft_runtime_signature_from_settings_and_sections(settings, sections, sc body = signature_add_value(body, "settings.game_console_ips", option(settings, "game_console_ips", "")); body = signature_add_value(body, "settings.excluded_clients", option(settings, "excluded_clients", "")); body = signature_add_value(body, "settings.excluded_ips", option(settings, "excluded_ips", "")); + body = signature_add_value(body, "settings.route_router_traffic", bool_option(settings, "route_router_traffic", false) ? "1" : "0"); + body = signature_add_value(body, "settings.route_router_traffic_section", option(settings, "route_router_traffic_section", "")); for (let section in sections) body = nft_rule_signature_body(body, object_or_empty(section)); @@ -3248,6 +3307,12 @@ else if (mode == "tproxy-route-rule-present") exit(tproxy_route_rule_present(ARGV[1], ARGV[2]) ? 0 : 1); else if (mode == "ensure-bridge-netfilter-disabled") exit(ensure_bridge_netfilter_disabled() ? 0 : 1); +else if (mode == "nft-enable-router-output-intercept") + exit(nft_enable_router_output_intercept(ARGV[1], ARGV[2], ARGV[3], ARGV[4]) ? 0 : 1); +else if (mode == "nft-disable-router-output-intercept") + exit(nft_disable_router_output_intercept(ARGV[1]) ? 0 : 1); +else if (mode == "nft-sync-router-output-intercept") + exit(nft_sync_router_output_intercept(ARGV[1], ARGV[2], ARGV[3]) ? 0 : 1); else { warn("Usage: nft/apply.uc ...\n"); exit(1); diff --git a/tachyon/files/usr/lib/service/i18n.uc b/tachyon/files/usr/lib/service/i18n.uc index f633da18..5d7dbe34 100644 --- a/tachyon/files/usr/lib/service/i18n.uc +++ b/tachyon/files/usr/lib/service/i18n.uc @@ -28,6 +28,8 @@ let strings = { isolate_p2p: "Isolate P2P", log_level: "Log Level", exclude_ntp: "Exclude NTP", + route_router_traffic: "Route Router Traffic", + route_router_traffic_section: "Router Traffic Section", shutdown_correctly: "Clean Shutdown", smart_detect: "Smart Detect", smart_detect_sections: "Smart Detect Sections", @@ -582,6 +584,8 @@ let strings = { isolate_p2p: "Изолировать P2P", log_level: "Уровень логов", exclude_ntp: "Исключить NTP", + route_router_traffic: "Маршрутизировать трафик роутера", + route_router_traffic_section: "Секция трафика роутера", shutdown_correctly: "Корректное завершение", smart_detect: "Smart Detect", smart_detect_sections: "Секции Smart Detect", diff --git a/tachyon/files/usr/lib/service/lifecycle.uc b/tachyon/files/usr/lib/service/lifecycle.uc index 6c7d6c37..c3920fd9 100644 --- a/tachyon/files/usr/lib/service/lifecycle.uc +++ b/tachyon/files/usr/lib/service/lifecycle.uc @@ -926,6 +926,8 @@ function start_main() { return status; } + module_status(NFT_UC, [ "nft-sync-router-output-intercept", NFT_TABLE_NAME, NFT_LOCALV4_SET_NAME, NFT_OUTBOUND_MARK ]); + status = module_status(PRIORITY_UC, [ "start-runtime" ]); if (status != 0) { log_message("Failed to start Priority runtime. Aborted.", "fatal"); @@ -1039,6 +1041,8 @@ function stop_main() { module_success(PARENTAL_QUOTA_UC, [ "remove-cron" ]); module_success(TELEGRAM_UC, [ "stop-runtime" ]); + module_success(NFT_UC, [ "nft-disable-router-output-intercept", NFT_TABLE_NAME ]); + if (command_success_from_args([ "nft", "list", "table", "inet", NFT_TABLE_NAME ])) { if (!command_success_from_args([ "nft", "delete", "table", "inet", NFT_TABLE_NAME ])) log_message("Failed to delete nft table " + NFT_TABLE_NAME, "warn"); @@ -1539,6 +1543,8 @@ function reload(reason) { module_success(PARENTAL_QUOTA_UC, [ "install-cron" ]); module_success(PARENTAL_QUOTA_UC, [ "tick" ]); + module_status(NFT_UC, [ "nft-sync-router-output-intercept", NFT_TABLE_NAME, NFT_LOCALV4_SET_NAME, NFT_OUTBOUND_MARK ]); + if (plan.needs_dnsmasq_configure == 1) { status = dnsmasq_configure(true); if (status != 0) diff --git a/tachyon/files/usr/lib/service/state.uc b/tachyon/files/usr/lib/service/state.uc index 7688804a..88806107 100644 --- a/tachyon/files/usr/lib/service/state.uc +++ b/tachyon/files/usr/lib/service/state.uc @@ -999,6 +999,8 @@ function nft_runtime_signature_body(settings, sections, schedules, profiles, gue body = signature_add_value(body, "settings.game_console_ips", option(settings, "game_console_ips", "")); body = signature_add_value(body, "settings.excluded_clients", option(settings, "excluded_clients", "")); body = signature_add_value(body, "settings.excluded_ips", option(settings, "excluded_ips", "")); + body = signature_add_value(body, "settings.route_router_traffic", bool_option(settings, "route_router_traffic", false) ? "1" : "0"); + body = signature_add_value(body, "settings.route_router_traffic_section", option(settings, "route_router_traffic_section", "")); for (let section in sections) { section = object_or_empty(section); @@ -1662,6 +1664,8 @@ function sing_box_signature_body(settings, sections, servers, mwan3_active, sche body = signature_add_value(body, "settings.download_lists_via_proxy_section", option(settings, "download_lists_via_proxy_section", "")); if (download_via_proxy_enabled(settings, "components")) body = signature_add_value(body, "settings.download_components_via_proxy_section", option(settings, "download_components_via_proxy_section", "")); + body = signature_add_value(body, "settings.route_router_traffic", bool_option_value(settings, "route_router_traffic", false)); + body = signature_add_value(body, "settings.route_router_traffic_section", option(settings, "route_router_traffic_section", "")); for (let section in sections) body = append_sing_box_rule_signature_body(body, object_or_empty(section), sections); diff --git a/tachyon/files/usr/lib/service/telegram.uc b/tachyon/files/usr/lib/service/telegram.uc index 95af1018..22bf4cd7 100644 --- a/tachyon/files/usr/lib/service/telegram.uc +++ b/tachyon/files/usr/lib/service/telegram.uc @@ -611,6 +611,8 @@ let setting_schema = { isolate_p2p: "isolate_p2p", log_level: "log_level", exclude_ntp: "exclude_ntp", + route_router_traffic: "route_router_traffic", + route_router_traffic_section: "route_router_traffic_section", shutdown_correctly: "shutdown_correctly", smart_detect: "smart_detect", smart_detect_sections: "smart_detect_sections" @@ -671,7 +673,7 @@ function is_boolean_key(key) { "dns_detour_enabled", "enable_output_network_interface", "enable_badwan_interface_monitoring", "enable_yacd", "disable_quic", "block_doh", "list_update_enabled", "component_update_check_enabled", "download_lists_via_proxy", "download_components_via_proxy", "dont_touch_dhcp", - "isolate_p2p", "exclude_ntp", "shutdown_correctly", "smart_detect", + "isolate_p2p", "exclude_ntp", "route_router_traffic", "shutdown_correctly", "smart_detect", "notify_crash", "notify_restart", "notify_server_switch", "notify_subscription", "notify_cert", "notify_dns_leak", "daily_report_enabled", "quiet_hours_enabled"]; for (let x in b) if (x == key) return true; diff --git a/tachyon/files/usr/lib/singbox/constants.uc b/tachyon/files/usr/lib/singbox/constants.uc index 68db6a08..ffbab043 100644 --- a/tachyon/files/usr/lib/singbox/constants.uc +++ b/tachyon/files/usr/lib/singbox/constants.uc @@ -28,6 +28,9 @@ const DNS_SOURCE6_SET = "tachyon_dns_sources6"; const SERVICE_MIXED_INBOUND_TAG = "service-mixed-in"; const SERVICE_MIXED_INBOUND_ADDRESS = "127.0.0.1"; const SERVICE_MIXED_INBOUND_PORT = 4534; +const REDIRECT_INBOUND_TAG = "redirect-in"; +const REDIRECT_INBOUND_ADDRESS = "127.0.0.1"; +const REDIRECT_INBOUND_PORT = 1604; const DIRECT_BYPASS_INBOUND_TAG = "direct-bypass-in"; const DIRECT_BYPASS_OUTBOUND_TAG = "direct-bypass-out"; const DIRECT_BYPASS_DEFAULT_PORT = 2080; @@ -142,6 +145,9 @@ return { SERVICE_MIXED_INBOUND_TAG, SERVICE_MIXED_INBOUND_ADDRESS, SERVICE_MIXED_INBOUND_PORT, + REDIRECT_INBOUND_TAG, + REDIRECT_INBOUND_ADDRESS, + REDIRECT_INBOUND_PORT, DIRECT_BYPASS_INBOUND_TAG, DIRECT_BYPASS_OUTBOUND_TAG, DIRECT_BYPASS_DEFAULT_PORT, diff --git a/tachyon/files/usr/lib/singbox/generator.uc b/tachyon/files/usr/lib/singbox/generator.uc index d59faba4..a428edc1 100644 --- a/tachyon/files/usr/lib/singbox/generator.uc +++ b/tachyon/files/usr/lib/singbox/generator.uc @@ -827,6 +827,42 @@ function add_service_mixed_proxy(config, settings, sections) { runtime_generate_unsupported("download components via proxy section is not set"); } +function router_traffic_section(settings) { + if (!bool_option(settings, "route_router_traffic", false)) + return ""; + return option(settings, "route_router_traffic_section", ""); +} + +function add_router_traffic_redirect(config, settings) { + let section_name = router_traffic_section(settings); + if (section_name == "") + return; + + push(config.inbounds, { + type: "redirect", + tag: runtime_constants.REDIRECT_INBOUND_TAG, + listen: runtime_constants.REDIRECT_INBOUND_ADDRESS, + listen_port: runtime_constants.REDIRECT_INBOUND_PORT + }); + + let sec_out = outbound_tag(section_name); + if (sec_out != "") { + let target_idx = 0; + if (type(config.route) == "object" && type(config.route.rules) == "array") { + for (let i = 0; i < length(config.route.rules); i++) { + let r = config.route.rules[i]; + if (r.action == "sniff" || r.action == "hijack-dns") + target_idx = i + 1; + } + splice(config.route.rules, target_idx, 0, { + action: "route", + inbound: [ runtime_constants.REDIRECT_INBOUND_TAG ], + outbound: sec_out + }); + } + } +} + // ─── Content blocking (parental control domains) ───────────────────────────── // Each enabled `config schedule` with blocked_domains generates: // 1. DNS rules (on the dedicated dns-block-in inbound) that reject the @@ -1368,6 +1404,7 @@ function generate_config(output_path, service_address, mwan3_active, supports_xh add_service_mixed_proxy(config, settings, sections); for (let section in sections) add_mixed_proxy_for_section(config, section, service_address); + add_router_traffic_redirect(config, settings); add_content_blocking(config); diff --git a/tachyon/files/usr/lib/singbox/route.uc b/tachyon/files/usr/lib/singbox/route.uc index c6386ce9..a306bcc7 100644 --- a/tachyon/files/usr/lib/singbox/route.uc +++ b/tachyon/files/usr/lib/singbox/route.uc @@ -78,6 +78,8 @@ function config(settings, runtime) { if (type(runtime) == "object" && type(runtime.dns_health_inbounds) == "array") for (let inbound in runtime.dns_health_inbounds) push(dns_hijack_inbounds, inbound); + if (bool_option(settings, "route_router_traffic", false) && option(settings, "route_router_traffic_section", "") != "") + push(sniff_inbounds, runtime_constants.REDIRECT_INBOUND_TAG); let rules = [ { action: "sniff", inbound: sniff_inbounds }, { action: "hijack-dns", inbound: dns_hijack_inbounds } diff --git a/tests/nft_apply.sh b/tests/nft_apply.sh index 83ee6876..d774ba57 100644 --- a/tests/nft_apply.sh +++ b/tests/nft_apply.sh @@ -687,6 +687,10 @@ br-lan tun0 [settings.excluded_ips] +[settings.route_router_traffic] +0 +[settings.route_router_traffic_section] + [rule.text_rule.action] bypass [rule.text_rule.ip_cidr] @@ -795,6 +799,10 @@ br-lan tun0 [settings.excluded_ips] +[settings.route_router_traffic] +0 +[settings.route_router_traffic_section] + [rule.enabled.action] bypass [rule.enabled.ip_cidr] @@ -853,6 +861,10 @@ br-lan [settings.excluded_ips] +[settings.route_router_traffic] +0 +[settings.route_router_traffic_section] + EOF_EXPECTED expected_signature="$(md5sum "$WORK_DIR/signature-defaults-expected.txt" | awk '{print $1}')" assert_eq "$expected_signature" \ diff --git a/tests/route_router_traffic.sh b/tests/route_router_traffic.sh new file mode 100644 index 00000000..776c1d5c --- /dev/null +++ b/tests/route_router_traffic.sh @@ -0,0 +1,137 @@ +#!/usr/bin/env bash +set -eo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +TACHYON_LIB="$ROOT_DIR/tachyon/files/usr/lib" +NFT_RUNTIME="$ROOT_DIR/tachyon/files/usr/lib/nft/apply.uc" +WORK_DIR="$(mktemp -d)" +NFT_LOG="$WORK_DIR/nft.log" +LOGGER_LOG="$WORK_DIR/logger.log" +export NFT_LOG LOGGER_LOG + +cleanup() { + rm -rf "$WORK_DIR" +} +trap cleanup EXIT + +fail() { + printf 'FAIL: %s\n' "$1" >&2 + printf 'nft log:\n' >&2 + cat "$NFT_LOG" >&2 2>/dev/null || true + exit 1 +} + +assert_contains() { + local file="$1" + local expected="$2" + local label="$3" + + grep -Fq "$expected" "$file" || fail "$label: expected '$expected'" +} + +mkdir -p "$WORK_DIR/bin" + +cat >"$WORK_DIR/bin/nft" <<'NFT' +#!/usr/bin/env bash +set -eo pipefail +{ + printf 'nft' + for arg in "$@"; do + printf '\t%s' "$arg" + done + printf '\n' +} >> "${NFT_LOG:?}" + +if [ "$#" -ge 5 ] && [ "$1" = "flush" ] && [ "$2" = "chain" ]; then + if [ ! -f "$WORK_DIR/chain_exists" ]; then + exit 1 + fi +fi + +if [ "$#" -ge 5 ] && [ "$1" = "add" ] && [ "$2" = "chain" ]; then + touch "$WORK_DIR/chain_exists" +fi + +if [ "$#" -ge 5 ] && [ "$1" = "delete" ] && [ "$2" = "chain" ]; then + rm -f "$WORK_DIR/chain_exists" +fi +NFT +chmod 0755 "$WORK_DIR/bin/nft" + +cat >"$WORK_DIR/bin/logger" <<'LOGGER' +#!/usr/bin/env bash +set -eo pipefail +printf 'logger\t%s\n' "$*" >> "${LOGGER_LOG:?}" +LOGGER +chmod 0755 "$WORK_DIR/bin/logger" + +export PATH="$WORK_DIR/bin:$PATH" + +nft_ucode() { + ucode -S -L "$TACHYON_LIB" "$NFT_RUNTIME" "$@" +} + +# Test 1: Direct enable call +: > "$NFT_LOG" +rm -f "$WORK_DIR/chain_exists" +nft_ucode nft-enable-router-output-intercept TachyonTable localv4 0x00100000 1 +assert_contains "$NFT_LOG" $'nft\tadd\tchain\tinet\tTachyonTable\toutput_redirect\t{ type nat hook output priority -100; policy accept; }' "enable: creates chain output_redirect" +assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tTachyonTable\toutput_redirect\tct\tstatus\tdnat\treturn' "enable: ct status dnat return" +assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tTachyonTable\toutput_redirect\tip\tdaddr\t@localv4\treturn' "enable: localv4 bypass" +assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tTachyonTable\toutput_redirect\ttcp\tdport\t53\treturn' "enable: port 53 bypass" +assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tTachyonTable\toutput_redirect\tudp\tdport\t123\treturn' "enable: exclude_ntp bypass" +assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tTachyonTable\toutput_redirect\tmeta\tmark\t0x00100000\treturn' "enable: outbound_mark bypass" +assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tTachyonTable\toutput_redirect\tmeta\tl4proto\ttcp\tcounter\tredirect\tto\t:1604' "enable: tcp redirect to 1604" + +# Test 2: Direct disable call +: > "$NFT_LOG" +nft_ucode nft-disable-router-output-intercept TachyonTable +assert_contains "$NFT_LOG" $'nft\tdelete\tchain\tinet\tTachyonTable\toutput_redirect' "disable: deletes chain output_redirect" + +# Test 3: Sync from UCI - when enabled +STATE_ENABLED="$WORK_DIR/enabled.state" +cat >"$STATE_ENABLED" <<'STATE' +tachyon.settings=settings +tachyon.settings.route_router_traffic=1 +tachyon.settings.route_router_traffic_section=proxy_sec +tachyon.settings.exclude_ntp=1 +tachyon.proxy_sec=section +tachyon.proxy_sec.enabled=1 +tachyon.proxy_sec.routing_mode=proxy +tachyon.proxy_sec.mark=354 +tachyon.proxy_sec.tproxy_port=10080 +STATE + +: > "$NFT_LOG" +rm -f "$WORK_DIR/chain_exists" +TACHYON_UCI_STATE_FILE="$STATE_ENABLED" nft_ucode nft-sync-router-output-intercept TachyonTable localv4 0x00100000 +assert_contains "$NFT_LOG" $'nft\tadd\tchain\tinet\tTachyonTable\toutput_redirect\t{ type nat hook output priority -100; policy accept; }' "sync(enabled): creates chain" +assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tTachyonTable\toutput_redirect\tmeta\tl4proto\ttcp\tcounter\tredirect\tto\t:1604' "sync(enabled): redirects to port" + +# Test 4: Sync from UCI - when disabled +STATE_DISABLED="$WORK_DIR/disabled.state" +cat >"$STATE_DISABLED" <<'STATE' +tachyon.settings=settings +tachyon.settings.route_router_traffic=0 +tachyon.settings.route_router_traffic_section=proxy_sec +tachyon.proxy_sec=section +tachyon.proxy_sec.enabled=1 +tachyon.proxy_sec.routing_mode=proxy +tachyon.proxy_sec.mark=354 +STATE + +: > "$NFT_LOG" +TACHYON_UCI_STATE_FILE="$STATE_DISABLED" nft_ucode nft-sync-router-output-intercept TachyonTable localv4 0x00100000 +assert_contains "$NFT_LOG" $'nft\tdelete\tchain\tinet\tTachyonTable\toutput_redirect' "sync(disabled): deletes chain" + +# Test 5: Verify UCI setting loads route_router_traffic +CHECK_SETTINGS=$(TACHYON_UCI_STATE_FILE="$STATE_DISABLED" ucode -S -L "$TACHYON_LIB" -e ' + let uci = require("core.uci"); + let val = uci.get("tachyon.settings.route_router_traffic"); + print("val=" + val); +') +if [ "$CHECK_SETTINGS" != "val=0" ]; then + fail "UCI route_router_traffic expected val=0, got '$CHECK_SETTINGS'" +fi + +printf 'ALL TESTS PASSED for route_router_traffic\n' diff --git a/tests/runtime_state_predicates.sh b/tests/runtime_state_predicates.sh index 79ecacc3..35f2a06d 100644 --- a/tests/runtime_state_predicates.sh +++ b/tests/runtime_state_predicates.sh @@ -591,6 +591,10 @@ secret 0 [settings.download_components_via_proxy] 0 +[settings.route_router_traffic] +0 +[settings.route_router_traffic_section] + [rule.proxy1.action] proxy [rule.proxy1.connection_urls]