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