diff --git a/examples/sing-box-vpn.json b/examples/sing-box-vpn.json index 20ee12f..ce3892f 100644 --- a/examples/sing-box-vpn.json +++ b/examples/sing-box-vpn.json @@ -1,14 +1,11 @@ { - "dns": { - "servers": [], - "rules": [], - "strategy": "ipv4_only" - }, "inbounds": [ { "type": "tun", "interface_name": "nekoray-tun", "inet4_address": "172.19.0.1/30", + %IPV6_ADDRESS% + "mtu": %MTU%, "auto_route": true, "sniff": false } diff --git a/examples/vpn-run-root.sh b/examples/vpn-run-root.sh index dcb9033..2fcf8b2 100755 --- a/examples/vpn-run-root.sh +++ b/examples/vpn-run-root.sh @@ -11,6 +11,7 @@ fi [ -z $TABLE_FWMARK ] && echo "Please set env TABLE_FWMARK" && exit [ -z $TUN_NAME ] && echo "Please set env TUN_NAME" && exit [ -z $USER_ID ] && echo "Please set env USER_ID" && exit +[ -z $MTU ] && MTU=1500 command -v pkill >/dev/null 2>&1 || exit BASEDIR=$(dirname "$0") @@ -19,16 +20,22 @@ cd $BASEDIR start() { # add tun (TODO the ip must be the same as matsuri) ip tuntap add $TUN_NAME mode tun user $USER_ID || return - ip addr add 172.19.0.1/30 dev $TUN_NAME || return + ip link set dev nekoray-tun mtu $MTU || return ip link set dev $TUN_NAME up || return # set ipv4 rule + ip addr add 172.19.0.1/30 dev $TUN_NAME || return ip rule add table $TABLE_FWMARK || return ip route add table $TABLE_FWMARK default dev $TUN_NAME || return - # set ipv6 unreachable + # set ipv6 rule ip -6 rule add table $TABLE_FWMARK || return - ip -6 route add table $TABLE_FWMARK unreachable default || return + if [ -z $ENABLE_IPV6 ]; then + ip -6 route add table $TABLE_FWMARK unreachable default || return + else + ip -6 addr add fdfe:dcba:9876::1/126 dev $TUN_NAME || return + ip -6 route add table $TABLE_FWMARK default dev $TUN_NAME || return + fi # set bypass: fwmark ip rule add fwmark $TABLE_FWMARK table main || return @@ -39,6 +46,10 @@ start() { ip rule add to $local table main done + # for Tun2Socket + iptables -I INPUT -s 172.19.0.2 -d 172.19.0.1 -p tcp -j ACCEPT + ip6tables -I INPUT -s fdfe:dcba:9876::2 -d fdfe:dcba:9876::1 -p tcp -j ACCEPT + if [ ! -z $USE_NEKORAY ]; then "./nekoray_core" tool protect --protect-listen-path "$PROTECT_LISTEN_PATH" --protect-fwmark $TABLE_FWMARK else @@ -55,6 +66,8 @@ stop() { for local in $BYPASS_IPS; do ip rule del to $local table main done + iptables -D INPUT -s 172.19.0.2 -d 172.19.0.1 -p tcp -j ACCEPT + ip6tables -D INPUT -s fdfe:dcba:9876::2 -d fdfe:dcba:9876::1 -p tcp -j ACCEPT ip rule del table $TABLE_FWMARK ip rule del fwmark $TABLE_FWMARK ip route del table $TABLE_FWMARK default diff --git a/go/tun_linux.go b/go/tun_linux.go index c380957..d16f59d 100644 --- a/go/tun_linux.go +++ b/go/tun_linux.go @@ -3,6 +3,7 @@ package main import ( "errors" "libcore" + "libcore/comm" "nekoray_core/gen" "sync" "syscall" @@ -33,6 +34,7 @@ func TunStart(config *gen.SetTunReq) (err error) { V2Ray: instance, // use current if started Implementation: config.Implementation, Sniffing: true, + IPv6Mode: comm.IPv6Prefer, FakeDNS: config.Fakedns, }) return diff --git a/main/NekoRay.cpp b/main/NekoRay.cpp index d5ac8d0..0f3e97e 100644 --- a/main/NekoRay.cpp +++ b/main/NekoRay.cpp @@ -50,6 +50,9 @@ namespace NekoRay { _add(new configItem("active_routing", &active_routing, itemType::string)); _add(new configItem("mw_size", &mw_size, itemType::string)); _add(new configItem("conn_stat", &connection_statistics, itemType::boolean)); + _add(new configItem("vpn_impl", &vpn_implementation, itemType::integer)); + _add(new configItem("vpn_mtu", &vpn_mtu, itemType::integer)); + _add(new configItem("vpn_ipv6", &vpn_ipv6, itemType::boolean)); } void DataStore::UpdateStartedId(int id) { diff --git a/main/NekoRay_DataStore.hpp b/main/NekoRay_DataStore.hpp index d81addf..54e7949 100644 --- a/main/NekoRay_DataStore.hpp +++ b/main/NekoRay_DataStore.hpp @@ -42,6 +42,14 @@ namespace NekoRay { int core_port = 19810; int started_id = -1919; + Routing *routing = new Routing; + int imported_count = 0; + bool refreshing_group_list = false; + + // Flags + bool flag_use_appdata = false; + bool flag_many = false; + // Saved // Misc @@ -91,6 +99,11 @@ namespace NekoRay { QString custom_route_global = "{\"rules\": []}"; QString active_routing = "Default"; + // VPN + int vpn_implementation = 0; + int vpn_mtu = 9000; + bool vpn_ipv6 = false; + // Hotkey QString hotkey_mainwindow = ""; QString hotkey_group = ""; @@ -99,19 +112,6 @@ namespace NekoRay { // Other Core ExtraCore *extraCore = new ExtraCore; - // Running Cache - - Routing *routing = new Routing; - int imported_count = 0; - bool refreshing_group_list = false; - - // Running Flags - - bool flag_use_appdata = false; - bool flag_many = false; - - // - DataStore(); void UpdateStartedId(int id); diff --git a/translations/zh_CN.ts b/translations/zh_CN.ts index 6fb99ef..8f94e61 100644 --- a/translations/zh_CN.ts +++ b/translations/zh_CN.ts @@ -450,6 +450,14 @@ Custom (global) 自定义 (全局) + + VPN Implementation + VPN 实现 + + + VPN Enable IPv6 + 启用 VPN IPv6 + EditChain @@ -1026,6 +1034,14 @@ End: %2 Enable VPN 启用 VPN + + VPN settings changed + VPN 设置改变 + + + Restart VPN to take effect. + 重启 VPN 生效。 + ProxyItem diff --git a/ui/dialog_manage_routes.cpp b/ui/dialog_manage_routes.cpp index f53534f..5e1e24c 100644 --- a/ui/dialog_manage_routes.cpp +++ b/ui/dialog_manage_routes.cpp @@ -38,14 +38,22 @@ DialogManageRoutes::DialogManageRoutes(QWidget *parent) : ui->dns_direct->setText(NekoRay::dataStore->direct_dns); ui->enhance_resolve_server_domain->setChecked(NekoRay::dataStore->enhance_resolve_server_domain); D_C_LOAD_STRING(custom_route_global) - + // + ui->vpn_implementation->setCurrentIndex(NekoRay::dataStore->vpn_implementation); + ui->vpn_mtu->setCurrentText(Int2String(NekoRay::dataStore->vpn_mtu)); + ui->vpn_ipv6->setChecked(NekoRay::dataStore->vpn_ipv6); +#ifdef Q_OS_WIN + ui->vpn_implementation->setEditable(true); + ui->vpn_implementation->setCurrentText("Windows: sing-box gVisor"); + ui->vpn_implementation->setDisabled(true); +#endif + // connect(ui->custom_route_edit, &QPushButton::clicked, this, [=] { C_EDIT_JSON_ALLOW_EMPTY(custom_route) }); connect(ui->custom_route_global_edit, &QPushButton::clicked, this, [=] { C_EDIT_JSON_ALLOW_EMPTY(custom_route_global) }); - // builtInSchemesMenu = new QMenu(this); builtInSchemesMenu->addActions(this->getBuiltInSchemes()); @@ -81,19 +89,39 @@ void DialogManageRoutes::accept() { NekoRay::dataStore->domain_strategy = ui->domainStrategyCombo->currentText(); NekoRay::dataStore->outbound_domain_strategy = ui->outbound_domain_strategy->currentText(); NekoRay::dataStore->dns_routing = ui->dns_routing->isChecked(); - NekoRay::dataStore->fake_dns = ui->fake_dns->isChecked(); NekoRay::dataStore->remote_dns = ui->dns_remote->text(); NekoRay::dataStore->direct_dns = ui->dns_direct->text(); NekoRay::dataStore->enhance_resolve_server_domain = ui->enhance_resolve_server_domain->isChecked(); D_C_SAVE_STRING(custom_route_global) - // + bool vpnChanged = false; + auto fakedns = ui->fake_dns->isChecked(); + auto mtu = ui->vpn_mtu->currentText().toInt(); + if (mtu > 10000 || mtu < 1000) mtu = 9000; + auto ipv6 = ui->vpn_ipv6->isChecked(); +#ifndef Q_OS_WIN + auto impl = ui->vpn_implementation->currentIndex(); + vpnChanged |= NekoRay::dataStore->vpn_implementation != impl; + NekoRay::dataStore->vpn_implementation = impl; +#endif + vpnChanged |= NekoRay::dataStore->fake_dns != fakedns; + vpnChanged |= NekoRay::dataStore->vpn_mtu != mtu; + vpnChanged |= NekoRay::dataStore->vpn_ipv6 != ipv6; + NekoRay::dataStore->fake_dns = fakedns; + NekoRay::dataStore->vpn_mtu = mtu; + NekoRay::dataStore->vpn_ipv6 = ipv6; + // + bool routeChanged = false; + if (NekoRay::dataStore->active_routing != active_routing) routeChanged = true; SAVE_TO_ROUTING(NekoRay::dataStore->routing) NekoRay::dataStore->active_routing = active_routing; NekoRay::dataStore->routing->fn = "routes/" + NekoRay::dataStore->active_routing; - NekoRay::dataStore->routing->Save(); - - dialog_message(Dialog_DialogManageRoutes, "UpdateDataStore"); + if (NekoRay::dataStore->routing->Save()) routeChanged = true; + // + QString info = "UpdateDataStore"; + if (routeChanged) info += "RouteChanged"; + if (vpnChanged) info += "VPNChanged"; + dialog_message(Dialog_DialogManageRoutes, info); QDialog::accept(); } diff --git a/ui/dialog_manage_routes.ui b/ui/dialog_manage_routes.ui index acc1acf..f3a3c1e 100644 --- a/ui/dialog_manage_routes.ui +++ b/ui/dialog_manage_routes.ui @@ -17,117 +17,227 @@ - - - - - Outbound Domain Strategy - - - - - + + + - - Disable - - - - - The sniffing result is used for routing - - - - - The sniffing result is used for destination - - - - - - - - Direct DNS - - - - - - - Sniffing Mode - - - - - - - Remote DNS - - - - - - - false - - - - AsIs - - - - - UseIP - - - - - UseIPv4 - - - - - UseIPv6 - - - - - PreferIPv4 - - - - - PreferIPv6 - - - - - - - - - - - - - 使用多个境外 DNS 查询服务器地址,一定程度上可缓解对服务器域名的 DNS 污染,可能有副作用。 + + + + 0 + 0 + - 增强域名解析 + Sniffing Mode + + + + + + + + Disable + + + + + The sniffing result is used for routing + + + + + The sniffing result is used for destination + + + + + + + + Qt::Vertical + + + + + + + + 0 + 0 + + + + Outbound Domain Strategy + + + + + + + false + + + + AsIs + + + + + UseIP + + + + + UseIPv4 + + + + + UseIPv6 + + + + + PreferIPv4 + + + + + PreferIPv6 + + + + + + + + + + + + + 0 + 0 + + + + VPN Implementation + + + + + + + + gVisor + + + + + System NAT + + + + + Tun2Socket + + + + + + + + + 0 + 0 + + + + MTU + + + + + + + true + + + + 9000 + + + + + 1500 + + + + + + + + + 0 + 0 + + + + VPN Enable IPv6 + + + + + + + + 0 + 0 + + + + FakeDNS - - + + + + + + Remote DNS + + + - + - FakeDNS + Direct DNS + + + + + + + + + + + 0 + 0 + + + + 使用多个境外 DNS 查询服务器地址,一定程度上可缓解对服务器域名的 DNS 污染,可能有副作用。 + + + 增强域名解析 @@ -141,6 +251,12 @@ + + + 0 + 0 + + Enable DNS Routing @@ -344,8 +460,10 @@ sniffing_mode outbound_domain_strategy - dns_remote + vpn_implementation + vpn_mtu fake_dns + dns_remote dns_direct enhance_resolve_server_domain dns_routing @@ -353,6 +471,8 @@ domainMatcherCombo preset custom_route_edit + custom_route_global_edit + load_save diff --git a/ui/edit/dialog_edit_group.ui b/ui/edit/dialog_edit_group.ui index 8bd020a..d74d021 100644 --- a/ui/edit/dialog_edit_group.ui +++ b/ui/edit/dialog_edit_group.ui @@ -35,9 +35,6 @@ 0 - - 0 - @@ -92,9 +89,6 @@ - - 6 - 0 diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index a74be5e..6be63eb 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -472,10 +472,13 @@ void MainWindow::show_group(int gid) { void MainWindow::dialog_message_impl(const QString &sender, const QString &info) { if (info.contains("UpdateDataStore")) { auto changed = NekoRay::dataStore->Save(); + if (info.contains("RouteChanged")) changed = true; refresh_proxy_list(); - if (changed && NekoRay::dataStore->started_id >= 0 && - QMessageBox::question(this, tr("Confirmation"), tr("Settings changed, restart proxy?") - ) == QMessageBox::StandardButton::Yes) { + if (info.contains("VPNChanged") && title_spmode == NekoRay::SystemProxyMode::VPN) { + MessageBoxWarning(tr("VPN settings changed"), tr("Restart VPN to take effect.")); + } else if (changed && NekoRay::dataStore->started_id >= 0 && + QMessageBox::question(this, tr("Confirmation"), tr("Settings changed, restart proxy?") + ) == QMessageBox::StandardButton::Yes) { neko_start(NekoRay::dataStore->started_id); } refresh_status(); @@ -1336,7 +1339,10 @@ bool MainWindow::StartVPNProcess() { #ifdef Q_OS_WIN auto configFn = ":/nekoray/vpn/sing-box-vpn.json"; if (QFile::exists("vpn/sing-box-vpn.json")) configFn = "vpn/sing-box-vpn.json"; - auto config = ReadFileText(configFn).replace("%PORT%", Int2String(NekoRay::dataStore->inbound_socks_port)); + auto config = ReadFileText(configFn) + .replace("%IPV6_ADDRESS%", NekoRay::dataStore->vpn_ipv6 ? "\"inet6_address\": \"fdfe:dcba:9876::1/128\"," : "") + .replace("%MTU%", Int2String(NekoRay::dataStore->vpn_mtu)) + .replace("%PORT%", Int2String(NekoRay::dataStore->inbound_socks_port)); #else auto protectPath = QDir::currentPath() + "/protect"; auto configFn = ":/nekoray/vpn/vpn-run-root.sh"; @@ -1344,11 +1350,13 @@ bool MainWindow::StartVPNProcess() { auto config = ReadFileText(configFn) .replace("$PORT", Int2String(NekoRay::dataStore->inbound_socks_port)) .replace("$USE_NEKORAY", "1") + .replace("$ENABLE_IPV6", NekoRay::dataStore->vpn_ipv6 ? "1" : "") .replace("./nekoray_core", QApplication::applicationDirPath() + "/nekoray_core") .replace("./tun2socks", QApplication::applicationDirPath() + "/tun2socks") .replace("$PROTECT_LISTEN_PATH", protectPath) .replace("$TUN_NAME", "nekoray-tun") .replace("$USER_ID", Int2String((int) getuid())) + .replace("$MTU", Int2String(NekoRay::dataStore->vpn_mtu)) .replace("$TABLE_FWMARK", "514"); #endif // diff --git a/ui/mainwindow_grpc.cpp b/ui/mainwindow_grpc.cpp index f57d3f1..19573ce 100644 --- a/ui/mainwindow_grpc.cpp +++ b/ui/mainwindow_grpc.cpp @@ -364,8 +364,8 @@ bool MainWindow::Tun2rayStartStop(bool start) { if (start) { libcore::SetTunReq req; req.set_name("nekoray-tun"); - req.set_mtu(1500); - req.set_implementation(0); + req.set_mtu(NekoRay::dataStore->vpn_mtu); + req.set_implementation(NekoRay::dataStore->vpn_implementation); req.set_fakedns(NekoRay::dataStore->fake_dns); auto error = defaultClient->SetTun(&ok, req); if (!ok) return false;