diff --git a/README.md b/README.md index 2eee55c6..ec1f829f 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Default settings are picked to suit general use cases and maximize compatibility I find turning this option off sometimes works better for dummy VPNs like ad-blockers and socksifiers than Simple mode, e.g. Shadowsocks. But you should never use this for real VPNs like OpenVPN, WireGuard, etc. - Simple: Source address/port from downstream packets will be remapped and that's about it. - - Android Netd Service: + - (since Android 9) Android Netd Service: Let your system handle masquerade. Android system will do a few extra things to make things like FTP and tethering traffic counter work. You should probably not use this if you are trying to hide your tethering activity from your carrier. diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RoutingManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RoutingManager.kt index c74dd731..2e20fcd2 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RoutingManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RoutingManager.kt @@ -1,5 +1,7 @@ package be.mygod.vpnhotspot +import android.annotation.TargetApi +import android.os.Build import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.net.Routing import be.mygod.vpnhotspot.net.wifi.WifiDoubleLock @@ -11,11 +13,16 @@ import timber.log.Timber abstract class RoutingManager(private val caller: Any, val downstream: String, private val isWifi: Boolean) { companion object { private const val KEY_MASQUERADE_MODE = "service.masqueradeMode" + private val masqueradeModeUnchecked: Routing.MasqueradeMode get() { + app.pref.getString(KEY_MASQUERADE_MODE, null)?.let { return Routing.MasqueradeMode.valueOf(it) } + return if (app.pref.getBoolean("service.masquerade", true)) // legacy settings + Routing.MasqueradeMode.Simple else Routing.MasqueradeMode.None + } var masqueradeMode: Routing.MasqueradeMode - get() { - app.pref.getString(KEY_MASQUERADE_MODE, null)?.let { return Routing.MasqueradeMode.valueOf(it) } - return if (app.pref.getBoolean("service.masquerade", true)) // legacy settings - Routing.MasqueradeMode.Simple else Routing.MasqueradeMode.None + @TargetApi(28) get() = masqueradeModeUnchecked.let { + // older app version enabled netd for everyone. should check again here + if (Build.VERSION.SDK_INT >= 28 || it != Routing.MasqueradeMode.Netd) it + else Routing.MasqueradeMode.Simple } set(value) = app.pref.edit().putString(KEY_MASQUERADE_MODE, value.name).apply() 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 351bdc13..9bee9b74 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/Routing.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/Routing.kt @@ -1,6 +1,8 @@ package be.mygod.vpnhotspot.net +import android.annotation.TargetApi 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 @@ -83,7 +85,16 @@ class Routing(private val caller: Any, private val downstream: String) : IpNeigh } enum class MasqueradeMode { - None, Simple, Netd + None, + Simple, + /** + * Netd does not support multiple tethering upstream below Android 9, which we heavily + * depend on. + * + * Source: https://android.googlesource.com/platform/system/netd/+/3b47c793ff7ade843b1d85a9be8461c3b4dc693e + */ + @RequiresApi(28) + Netd } class InterfaceNotFoundException(override val cause: Throwable) : SocketException() { @@ -116,7 +127,7 @@ class Routing(private val caller: Any, private val downstream: String) : IpNeigh iptablesAdd(if (upstream == null) "vpnhotspot_masquerade -s $hostSubnet -j MASQUERADE" else "vpnhotspot_masquerade -s $hostSubnet -o $upstream -j MASQUERADE", "nat") } - when (masqueradeMode) { + @TargetApi(28) when (masqueradeMode) { MasqueradeMode.None -> { } // nothing to be done here MasqueradeMode.Simple -> simpleMasquerade() // fallback is only needed for repeater on API 23 @@ -303,6 +314,7 @@ class Routing(private val caller: Any, private val downstream: String) : IpNeigh fun commit(localOnly: Boolean = false) { transaction.commit() Timber.i("Started routing for $downstream by $caller") + @TargetApi(28) if (localOnly || masqueradeMode != MasqueradeMode.Netd) DefaultNetworkMonitor.registerCallback(fallbackUpstream) UpstreamMonitor.registerCallback(upstream) IpNeighbourMonitor.registerCallback(this) diff --git a/mobile/src/main/res/values-v28/arrays.xml b/mobile/src/main/res/values-v28/arrays.xml new file mode 100644 index 00000000..d37b03dd --- /dev/null +++ b/mobile/src/main/res/values-v28/arrays.xml @@ -0,0 +1,13 @@ + + + + @string/settings_service_masquerade_none + @string/settings_service_masquerade_simple + @string/settings_service_masquerade_netd + + + None + Simple + Netd + + diff --git a/mobile/src/main/res/values/arrays.xml b/mobile/src/main/res/values/arrays.xml index 2621de2d..fc606373 100644 --- a/mobile/src/main/res/values/arrays.xml +++ b/mobile/src/main/res/values/arrays.xml @@ -1,14 +1,12 @@ - - + + @string/settings_service_masquerade_none @string/settings_service_masquerade_simple - @string/settings_service_masquerade_netd - + None Simple - Netd