From 5583e742caabe838ddaa3dbac65b8ff3bd38ad73 Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 15 Jul 2019 23:28:48 +0800 Subject: [PATCH] Revert "Remove fallback upstream monitor" This commit reverts 99e721bf7e9e81d6e47b5e7d7c6e1a64f8d84ee0. Fixes #119. --- .../be/mygod/vpnhotspot/RoutingManager.kt | 2 +- .../vpnhotspot/SettingsPreferenceFragment.kt | 37 +++++++++-------- .../java/be/mygod/vpnhotspot/net/Routing.kt | 40 ++++++++++-------- .../net/monitor/FallbackUpstreamMonitor.kt | 41 +++++++++++++++++++ .../vpnhotspot/net/monitor/TrafficRecorder.kt | 2 +- .../ic_action_settings_input_component.xml | 10 +++++ mobile/src/main/res/values-zh-rCN/strings.xml | 2 + mobile/src/main/res/values/strings.xml | 2 + mobile/src/main/res/xml/pref_settings.xml | 5 +++ 9 files changed, 107 insertions(+), 34 deletions(-) create mode 100644 mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/FallbackUpstreamMonitor.kt create mode 100644 mobile/src/main/res/drawable/ic_action_settings_input_component.xml diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RoutingManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RoutingManager.kt index e40c04e1..44a3acb6 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RoutingManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RoutingManager.kt @@ -48,7 +48,7 @@ abstract class RoutingManager(private val caller: Any, val downstream: String, p ipForward() // local only interfaces need to enable ip_forward forward() masquerade(masqueradeMode) - commit(true) + commit() } } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/SettingsPreferenceFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/SettingsPreferenceFragment.kt index e41afb88..a6dd90bf 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/SettingsPreferenceFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/SettingsPreferenceFragment.kt @@ -11,6 +11,7 @@ import androidx.preference.SwitchPreference import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.net.Routing.Companion.IPTABLES import be.mygod.vpnhotspot.net.TetherOffloadManager +import be.mygod.vpnhotspot.net.monitor.FallbackUpstreamMonitor import be.mygod.vpnhotspot.net.monitor.IpMonitor import be.mygod.vpnhotspot.net.monitor.UpstreamMonitor import be.mygod.vpnhotspot.net.wifi.WifiDoubleLock @@ -115,6 +116,9 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() { |echo iptables -nvx -L vpnhotspot_fwd |$IPTABLES -nvx -L vpnhotspot_fwd |echo + |echo iptables -nvx -L vpnhotspot_acl + |$IPTABLES -nvx -L vpnhotspot_acl + |echo |echo logcat-su |logcat -d """.trimMargin()) @@ -146,23 +150,24 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() { override fun onDisplayPreferenceDialog(preference: Preference) { when (preference.key) { - UpstreamMonitor.KEY -> AlwaysAutoCompleteEditTextPreferenceDialogFragmentCompat().apply { - setArguments(preference.key, try { - NetworkInterface.getNetworkInterfaces().asSequence() - .filter { - try { - it.isUp && !it.isLoopback && it.interfaceAddresses.isNotEmpty() - } catch (_: SocketException) { - false + UpstreamMonitor.KEY, FallbackUpstreamMonitor.KEY -> + AlwaysAutoCompleteEditTextPreferenceDialogFragmentCompat().apply { + setArguments(preference.key, try { + NetworkInterface.getNetworkInterfaces().asSequence() + .filter { + try { + it.isUp && !it.isLoopback && it.interfaceAddresses.isNotEmpty() + } catch (_: SocketException) { + false + } } - } - .map { it.name }.sorted().toList().toTypedArray() - } catch (e: SocketException) { - Timber.d(e) - emptyArray() - }) - setTargetFragment(this@SettingsPreferenceFragment, 0) - }.show(fragmentManager ?: return, preference.key) + .map { it.name }.sorted().toList().toTypedArray() + } catch (e: SocketException) { + Timber.d(e) + emptyArray() + }) + setTargetFragment(this@SettingsPreferenceFragment, 0) + }.show(fragmentManager ?: return, preference.key) else -> super.onDisplayPreferenceDialog(preference) } } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/Routing.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/Routing.kt index 104b2f64..f5ddb6bb 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/Routing.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/Routing.kt @@ -5,7 +5,7 @@ import android.os.Build import androidx.annotation.RequiresApi import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.R -import be.mygod.vpnhotspot.net.monitor.DefaultNetworkMonitor +import be.mygod.vpnhotspot.net.monitor.FallbackUpstreamMonitor import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor import be.mygod.vpnhotspot.net.monitor.TrafficRecorder import be.mygod.vpnhotspot.net.monitor.UpstreamMonitor @@ -52,6 +52,8 @@ class Routing(private val caller: Any, private val downstream: String) : IpNeigh it.execQuiet("while $IPTABLES -D FORWARD -j vpnhotspot_fwd; do done") it.execQuiet("$IPTABLES -F vpnhotspot_fwd") it.execQuiet("$IPTABLES -X vpnhotspot_fwd") + it.execQuiet("$IPTABLES -F vpnhotspot_acl") + it.execQuiet("$IPTABLES -X vpnhotspot_acl") it.execQuiet("while $IPTABLES -t nat -D POSTROUTING -j vpnhotspot_masquerade; do done") it.execQuiet("$IPTABLES -t nat -F vpnhotspot_masquerade") it.execQuiet("$IPTABLES -t nat -X vpnhotspot_masquerade") @@ -120,17 +122,23 @@ class Routing(private val caller: Any, private val downstream: String) : IpNeigh */ inner class Subrouting(priority: Int, val upstream: String? = null) { val transaction = RootSession.beginTransaction().safeguard { - if (upstream != null) ipRule("from all", downstream, upstream, priority) - fun simpleMasquerade() { - // note: specifying -i wouldn't work for POSTROUTING - iptablesAdd(if (upstream == null) "vpnhotspot_masquerade -s $hostSubnet -j MASQUERADE" else - "vpnhotspot_masquerade -s $hostSubnet -o $upstream -j MASQUERADE", "nat") + if (upstream != null) { + ipRule("from all", downstream, upstream, priority) + iptablesInsert("vpnhotspot_fwd -i $downstream -o $upstream -j vpnhotspot_acl") + iptablesInsert("vpnhotspot_fwd -i $upstream -o $downstream -m state --state ESTABLISHED,RELATED -j vpnhotspot_acl") + } else { + iptablesInsert("vpnhotspot_fwd -i $downstream -j vpnhotspot_acl") + iptablesInsert("vpnhotspot_fwd -o $downstream -m state --state ESTABLISHED,RELATED -j vpnhotspot_acl") } @TargetApi(28) when (masqueradeMode) { MasqueradeMode.None -> { } // nothing to be done here - MasqueradeMode.Simple -> simpleMasquerade() - // fallback is only needed for repeater on API 23 - MasqueradeMode.Netd -> if (upstream == null) simpleMasquerade() else { + MasqueradeMode.Simple -> { + // note: specifying -i wouldn't work for POSTROUTING + iptablesAdd(if (upstream == null) "vpnhotspot_masquerade -s $hostSubnet -j MASQUERADE" else + "vpnhotspot_masquerade -s $hostSubnet -o $upstream -j MASQUERADE", "nat") + } + MasqueradeMode.Netd -> { + check(upstream != null) // fallback is only needed for repeater on API 23 < 28 /** * 0 means that there are no interface addresses coming after, which is unused anyway. * @@ -166,7 +174,7 @@ class Routing(private val caller: Any, private val downstream: String) : IpNeigh override fun onLost() = synchronized(this@Routing) { val subrouting = subrouting ?: return // we could be removing fallback subrouting which no collision could ever happen, check before removing - if (subrouting.upstream != null) check(upstreams.remove(subrouting.upstream)) + subrouting.upstream?.let { check(upstreams.remove(it)) } subrouting.transaction.revert() this.subrouting = null dns = emptyList() @@ -191,8 +199,8 @@ class Routing(private val caller: Any, private val downstream: String) : IpNeigh private inner class Client(private val ip: Inet4Address, mac: Long) : AutoCloseable { private val transaction = RootSession.beginTransaction().safeguard { val address = ip.hostAddress - iptablesInsert("vpnhotspot_fwd -i $downstream -s $address -j ACCEPT") - iptablesInsert("vpnhotspot_fwd -o $downstream -d $address -m state --state ESTABLISHED,RELATED -j ACCEPT") + iptablesInsert("vpnhotspot_acl -i $downstream -s $address -j ACCEPT") + iptablesInsert("vpnhotspot_acl -o $downstream -d $address -j ACCEPT") } init { @@ -261,6 +269,7 @@ class Routing(private val caller: Any, private val downstream: String) : IpNeigh fun forward() { transaction.execQuiet("$IPTABLES -N vpnhotspot_fwd") + transaction.execQuiet("$IPTABLES -N vpnhotspot_acl") transaction.iptablesInsert("FORWARD -j vpnhotspot_fwd") transaction.iptablesAdd("vpnhotspot_fwd -i $downstream ! -o $downstream -j DROP") // ensure blocking works // the real forwarding filters will be added in Subrouting when clients are connected @@ -306,15 +315,14 @@ class Routing(private val caller: Any, private val downstream: String) : IpNeigh fun stop() { IpNeighbourMonitor.unregisterCallback(this) - DefaultNetworkMonitor.unregisterCallback(fallbackUpstream) + FallbackUpstreamMonitor.unregisterCallback(fallbackUpstream) UpstreamMonitor.unregisterCallback(upstream) } - fun commit(localOnly: Boolean = false) { + fun commit() { transaction.commit() Timber.i("Started routing for $downstream by $caller") - @TargetApi(28) - if (localOnly || masqueradeMode != MasqueradeMode.Netd) DefaultNetworkMonitor.registerCallback(fallbackUpstream) + FallbackUpstreamMonitor.registerCallback(fallbackUpstream) UpstreamMonitor.registerCallback(upstream) IpNeighbourMonitor.registerCallback(this) } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/FallbackUpstreamMonitor.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/FallbackUpstreamMonitor.kt new file mode 100644 index 00000000..b6eb2c50 --- /dev/null +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/FallbackUpstreamMonitor.kt @@ -0,0 +1,41 @@ +package be.mygod.vpnhotspot.net.monitor + +import android.content.SharedPreferences +import be.mygod.vpnhotspot.App.Companion.app + +abstract class FallbackUpstreamMonitor private constructor() : UpstreamMonitor() { + companion object : SharedPreferences.OnSharedPreferenceChangeListener { + const val KEY = "service.upstream.fallback" + + init { + app.pref.registerOnSharedPreferenceChangeListener(this) + } + + private fun generateMonitor(): UpstreamMonitor { + val upstream = app.pref.getString(KEY, null) + return if (upstream.isNullOrEmpty()) DefaultNetworkMonitor else InterfaceMonitor(upstream) + } + private var monitor = generateMonitor() + + fun registerCallback(callback: Callback) = synchronized(this) { monitor.registerCallback(callback) } + fun unregisterCallback(callback: Callback) = synchronized(this) { monitor.unregisterCallback(callback) } + + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { + if (key == KEY) synchronized(this) { + val old = monitor + val callbacks = synchronized(old) { + val callbacks = old.callbacks.toList() + old.callbacks.clear() + old.destroyLocked() + callbacks + } + val new = generateMonitor() + monitor = new + for (callback in callbacks) { + callback.onLost() + new.registerCallback(callback) + } + } + } + } +} diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/TrafficRecorder.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/TrafficRecorder.kt index 0d98bc55..53b501e2 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/TrafficRecorder.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/TrafficRecorder.kt @@ -62,7 +62,7 @@ object TrafficRecorder { private fun doUpdate(timestamp: Long) { val oldRecords = LongSparseArray() loop@ for (line in RootSession.use { - val command = "$IPTABLES -nvx -L vpnhotspot_fwd" + val command = "$IPTABLES -nvx -L vpnhotspot_acl" val result = it.execQuiet(command) val message = RootSession.checkOutput(command, result, false, false) if (result.err.isNotEmpty()) Timber.i(message) diff --git a/mobile/src/main/res/drawable/ic_action_settings_input_component.xml b/mobile/src/main/res/drawable/ic_action_settings_input_component.xml new file mode 100644 index 00000000..2a87e5bd --- /dev/null +++ b/mobile/src/main/res/drawable/ic_action_settings_input_component.xml @@ -0,0 +1,10 @@ + + + diff --git a/mobile/src/main/res/values-zh-rCN/strings.xml b/mobile/src/main/res/values-zh-rCN/strings.xml index de7f442a..158486cc 100644 --- a/mobile/src/main/res/values-zh-rCN/strings.xml +++ b/mobile/src/main/res/values-zh-rCN/strings.xml @@ -99,6 +99,8 @@ 轮询 (root) 上游网络接口 自动检测系统 VPN + 备用上游接口 + 自动检测系统默认网络 清理/重新应用路由规则 将修改的设置应用到当前启用的服务上。也可用于修复偶尔会发生的竞态条件。 尝试修复 DHCP diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml index 3611cba4..2d09b436 100644 --- a/mobile/src/main/res/values/strings.xml +++ b/mobile/src/main/res/values/strings.xml @@ -104,6 +104,8 @@ Poll with root Upstream network interface Auto detect system VPN + Fallback upstream interface + Auto detect system default network Enable DHCP workaround Use this if clients cannot obtain IP addresses. Clean/reapply routing rules diff --git a/mobile/src/main/res/xml/pref_settings.xml b/mobile/src/main/res/xml/pref_settings.xml index a9219dff..6c496cd7 100644 --- a/mobile/src/main/res/xml/pref_settings.xml +++ b/mobile/src/main/res/xml/pref_settings.xml @@ -12,6 +12,11 @@ app:icon="@drawable/ic_action_settings_ethernet" app:title="@string/settings_service_upstream" app:summary="@string/settings_service_upstream_auto"/> +