Add netd masquerade mode
This commit is contained in:
@@ -80,8 +80,6 @@ class App : Application() {
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
val masquerade get() = pref.getBoolean("service.masquerade", true)
|
|
||||||
|
|
||||||
val onPreCleanRoutings = Event0()
|
val onPreCleanRoutings = Event0()
|
||||||
val onRoutingsCleaned = Event0()
|
val onRoutingsCleaned = Event0()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ class LocalOnlyInterfaceManager(val downstream: String) {
|
|||||||
routing = try {
|
routing = try {
|
||||||
Routing(downstream, owner).apply {
|
Routing(downstream, owner).apply {
|
||||||
try {
|
try {
|
||||||
ipForward() // local only interfaces need to enable ip_forward
|
ipForward() // local only interfaces need to enable ip_forward
|
||||||
forward()
|
forward()
|
||||||
if (app.masquerade) masquerade()
|
masquerade(Routing.masquerade)
|
||||||
commit()
|
commit(true)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
revert()
|
revert()
|
||||||
throw e
|
throw e
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import java.net.SocketException
|
|||||||
class SettingsPreferenceFragment : PreferenceFragmentCompat() {
|
class SettingsPreferenceFragment : PreferenceFragmentCompat() {
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
preferenceManager.preferenceDataStore = SharedPreferenceDataStore(app.pref)
|
preferenceManager.preferenceDataStore = SharedPreferenceDataStore(app.pref)
|
||||||
|
Routing.masquerade = Routing.masquerade // flush default value
|
||||||
addPreferencesFromResource(R.xml.pref_settings)
|
addPreferencesFromResource(R.xml.pref_settings)
|
||||||
val boot = findPreference("service.repeater.startOnBoot") as SwitchPreference
|
val boot = findPreference("service.repeater.startOnBoot") as SwitchPreference
|
||||||
if (RepeaterService.supported) {
|
if (RepeaterService.supported) {
|
||||||
|
|||||||
@@ -69,10 +69,8 @@ class TetheringService : IpNeighbourMonitoringService() {
|
|||||||
try {
|
try {
|
||||||
routings[downstream] = Routing(downstream).apply {
|
routings[downstream] = Routing(downstream).apply {
|
||||||
try {
|
try {
|
||||||
// system tethering already has working forwarding rules
|
|
||||||
// so it doesn't make sense to add additional forwarding rules
|
|
||||||
forward()
|
forward()
|
||||||
if (app.masquerade) masquerade()
|
masquerade(Routing.masquerade)
|
||||||
if (disableIpv6) disableIpv6()
|
if (disableIpv6) disableIpv6()
|
||||||
commit()
|
commit()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ class Routing(val downstream: String, ownerAddress: InterfaceAddress? = null) :
|
|||||||
* Since Android 5.0, RULE_PRIORITY_TETHERING = 18000.
|
* Since Android 5.0, RULE_PRIORITY_TETHERING = 18000.
|
||||||
* This also works for Wi-Fi direct where there's no rule at 18000.
|
* This also works for Wi-Fi direct where there's no rule at 18000.
|
||||||
*
|
*
|
||||||
|
* We override system tethering rules by adding our own rules at higher priority.
|
||||||
|
*
|
||||||
* Source: https://android.googlesource.com/platform/system/netd/+/b9baf26/server/RouteController.cpp#65
|
* Source: https://android.googlesource.com/platform/system/netd/+/b9baf26/server/RouteController.cpp#65
|
||||||
*/
|
*/
|
||||||
private const val RULE_PRIORITY_UPSTREAM = 17800
|
private const val RULE_PRIORITY_UPSTREAM = 17800
|
||||||
@@ -39,6 +41,15 @@ class Routing(val downstream: String, ownerAddress: InterfaceAddress? = null) :
|
|||||||
*/
|
*/
|
||||||
val IPTABLES = if (Build.VERSION.SDK_INT >= 26) "iptables -w 1" else "iptables -w"
|
val IPTABLES = if (Build.VERSION.SDK_INT >= 26) "iptables -w 1" else "iptables -w"
|
||||||
const val KEY_DNS = "service.dns"
|
const val KEY_DNS = "service.dns"
|
||||||
|
const val KEY_MASQUERADE = "service.masqueradeMode"
|
||||||
|
|
||||||
|
var masquerade: MasqueradeMode
|
||||||
|
get() {
|
||||||
|
app.pref.getString(KEY_MASQUERADE, null)?.let { return MasqueradeMode.valueOf(it) }
|
||||||
|
return if (app.pref.getBoolean("service.masquerade", true)) // legacy settings
|
||||||
|
MasqueradeMode.Simple else MasqueradeMode.None
|
||||||
|
}
|
||||||
|
set(value) = app.pref.edit().putString(KEY_MASQUERADE, value.name).apply()
|
||||||
|
|
||||||
fun clean() {
|
fun clean() {
|
||||||
TrafficRecorder.clean()
|
TrafficRecorder.clean()
|
||||||
@@ -64,6 +75,15 @@ class Routing(val downstream: String, ownerAddress: InterfaceAddress? = null) :
|
|||||||
iptables("$IPTABLES -t $table -A $content", "$IPTABLES -t $table -D $content")
|
iptables("$IPTABLES -t $table -A $content", "$IPTABLES -t $table -D $content")
|
||||||
private fun RootSession.Transaction.iptablesInsert(content: String, table: String = "filter") =
|
private fun RootSession.Transaction.iptablesInsert(content: String, table: String = "filter") =
|
||||||
iptables("$IPTABLES -t $table -I $content", "$IPTABLES -t $table -D $content")
|
iptables("$IPTABLES -t $table -I $content", "$IPTABLES -t $table -D $content")
|
||||||
|
|
||||||
|
private fun RootSession.Transaction.ndc(name: String, command: String, revert: String) {
|
||||||
|
val result = execQuiet(command, revert)
|
||||||
|
RootSession.checkOutput(command, result, result.out.joinToString("\n") != "200 0 $name operation succeeded")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class MasqueradeMode {
|
||||||
|
None, Simple, Netd
|
||||||
}
|
}
|
||||||
|
|
||||||
class InterfaceNotFoundException : SocketException() {
|
class InterfaceNotFoundException : SocketException() {
|
||||||
@@ -75,7 +95,7 @@ class Routing(val downstream: String, ownerAddress: InterfaceAddress? = null) :
|
|||||||
val hostSubnet = "${hostAddress.address.hostAddress}/${hostAddress.networkPrefixLength}"
|
val hostSubnet = "${hostAddress.address.hostAddress}/${hostAddress.networkPrefixLength}"
|
||||||
private val transaction = RootSession.beginTransaction()
|
private val transaction = RootSession.beginTransaction()
|
||||||
|
|
||||||
var hasMasquerade = false
|
var masqueradeMode = MasqueradeMode.None
|
||||||
|
|
||||||
private val upstreams = HashSet<String>()
|
private val upstreams = HashSet<String>()
|
||||||
private open inner class Upstream(val priority: Int) : UpstreamMonitor.Callback {
|
private open inner class Upstream(val priority: Int) : UpstreamMonitor.Callback {
|
||||||
@@ -89,10 +109,22 @@ class Routing(val downstream: String, ownerAddress: InterfaceAddress? = null) :
|
|||||||
// by the time stopScript is called, table entry for upstream may already get removed
|
// by the time stopScript is called, table entry for upstream may already get removed
|
||||||
"ip rule del from all iif $downstream priority $priority")
|
"ip rule del from all iif $downstream priority $priority")
|
||||||
}
|
}
|
||||||
// note: specifying -i wouldn't work for POSTROUTING
|
when (masqueradeMode) {
|
||||||
if (hasMasquerade) {
|
// note: specifying -i wouldn't work for POSTROUTING
|
||||||
iptablesAdd(if (upstream == null) "vpnhotspot_masquerade -s $hostSubnet -j MASQUERADE" else
|
MasqueradeMode.Simple -> {
|
||||||
"vpnhotspot_masquerade -s $hostSubnet -o $upstream -j MASQUERADE", "nat")
|
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)
|
||||||
|
/**
|
||||||
|
* 0 means that there are no interface addresses coming after, which is unused anyway.
|
||||||
|
*
|
||||||
|
* https://android.googlesource.com/platform/frameworks/base/+/android-5.0.0_r1/services/core/java/com/android/server/NetworkManagementService.java#1251
|
||||||
|
* https://android.googlesource.com/platform/system/netd/+/android-5.0.0_r1/server/CommandListener.cpp#638
|
||||||
|
*/
|
||||||
|
ndc("Nat", "ndc nat enable $downstream $upstream 0", "ndc nat disable $downstream $upstream 0")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,10 +228,8 @@ class Routing(val downstream: String, ownerAddress: InterfaceAddress? = null) :
|
|||||||
*/
|
*/
|
||||||
fun ipForward() {
|
fun ipForward() {
|
||||||
if (Build.VERSION.SDK_INT >= 23) {
|
if (Build.VERSION.SDK_INT >= 23) {
|
||||||
val command = "ndc ipfwd enable vpnhotspot_$downstream"
|
transaction.ndc("ipfwd", "ndc ipfwd enable vpnhotspot_$downstream",
|
||||||
val result = transaction.execQuiet(command, "ndc ipfwd disable vpnhotspot_$downstream")
|
"ndc ipfwd disable vpnhotspot_$downstream")
|
||||||
RootSession.checkOutput(command, result, result.out.joinToString("\n") !=
|
|
||||||
"200 0 ipfwd operation succeeded")
|
|
||||||
} else transaction.exec("echo 1 >/proc/sys/net/ipv4/ip_forward")
|
} else transaction.exec("echo 1 >/proc/sys/net/ipv4/ip_forward")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,11 +248,13 @@ class Routing(val downstream: String, ownerAddress: InterfaceAddress? = null) :
|
|||||||
// the real forwarding filters will be added in Subrouting when clients are connected
|
// the real forwarding filters will be added in Subrouting when clients are connected
|
||||||
}
|
}
|
||||||
|
|
||||||
fun masquerade() {
|
fun masquerade(mode: MasqueradeMode) {
|
||||||
transaction.execQuiet("$IPTABLES -t nat -N vpnhotspot_masquerade")
|
masqueradeMode = mode
|
||||||
transaction.iptablesInsert("POSTROUTING -j vpnhotspot_masquerade", "nat")
|
if (mode == MasqueradeMode.Simple) {
|
||||||
hasMasquerade = true
|
transaction.execQuiet("$IPTABLES -t nat -N vpnhotspot_masquerade")
|
||||||
// further rules are added when upstreams are found
|
transaction.iptablesInsert("POSTROUTING -j vpnhotspot_masquerade", "nat")
|
||||||
|
// further rules are added when upstreams are found
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class DnsRoute(val dns: String) {
|
private inner class DnsRoute(val dns: String) {
|
||||||
@@ -255,10 +287,12 @@ class Routing(val downstream: String, ownerAddress: InterfaceAddress? = null) :
|
|||||||
UpstreamMonitor.unregisterCallback(upstream)
|
UpstreamMonitor.unregisterCallback(upstream)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun commit() {
|
fun commit(localOnly: Boolean = false) {
|
||||||
transaction.commit()
|
transaction.commit()
|
||||||
Timber.i("Started routing for $downstream")
|
Timber.i("Started routing for $downstream")
|
||||||
FallbackUpstreamMonitor.registerCallback(fallbackUpstream)
|
if (localOnly || masqueradeMode != MasqueradeMode.Netd) {
|
||||||
|
FallbackUpstreamMonitor.registerCallback(fallbackUpstream)
|
||||||
|
}
|
||||||
UpstreamMonitor.registerCallback(upstream)
|
UpstreamMonitor.registerCallback(upstream)
|
||||||
IpNeighbourMonitor.registerCallback(this)
|
IpNeighbourMonitor.registerCallback(this)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,8 +80,10 @@
|
|||||||
|
|
||||||
<string name="settings_upstream">上游</string>
|
<string name="settings_upstream">上游</string>
|
||||||
<string name="settings_downstream">下游</string>
|
<string name="settings_downstream">下游</string>
|
||||||
<string name="settings_service_masquerade">IP 掩蔽</string>
|
<string name="settings_service_masquerade">IP 掩蔽模式</string>
|
||||||
<string name="settings_service_masquerade_summary">建议使用广告拦截器与 socksfier 等虚拟 VPN 应用时禁用此选项。</string>
|
<string name="settings_service_masquerade_none">无</string>
|
||||||
|
<string name="settings_service_masquerade_simple">简易</string>
|
||||||
|
<string name="settings_service_masquerade_netd">Android Netd 服务</string>
|
||||||
<string name="settings_service_repeater_oc">Wi\u2011Fi 运行频段 (不稳定)</string>
|
<string name="settings_service_repeater_oc">Wi\u2011Fi 运行频段 (不稳定)</string>
|
||||||
<string name="settings_service_repeater_oc_summary">"自动 (1\u201114 = 2.4GHz, 15\u2011165 = 5GHz)"</string>
|
<string name="settings_service_repeater_oc_summary">"自动 (1\u201114 = 2.4GHz, 15\u2011165 = 5GHz)"</string>
|
||||||
<string name="settings_service_disable_ipv6">禁用 IPv6 共享</string>
|
<string name="settings_service_disable_ipv6">禁用 IPv6 共享</string>
|
||||||
|
|||||||
@@ -1,5 +1,16 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
<string-array name="settings_service_masquerade">
|
||||||
|
<item>@string/settings_service_masquerade_none</item>
|
||||||
|
<item>@string/settings_service_masquerade_simple</item>
|
||||||
|
<item>@string/settings_service_masquerade_netd</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="settings_service_masquerade_values">
|
||||||
|
<item>None</item>
|
||||||
|
<item>Simple</item>
|
||||||
|
<item>Netd</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
<string-array name="settings_service_wifi_lock">
|
<string-array name="settings_service_wifi_lock">
|
||||||
<item>@string/settings_service_wifi_lock_none</item>
|
<item>@string/settings_service_wifi_lock_none</item>
|
||||||
<item>@string/settings_service_wifi_lock_full</item>
|
<item>@string/settings_service_wifi_lock_full</item>
|
||||||
@@ -10,6 +21,7 @@
|
|||||||
<item>Full</item>
|
<item>Full</item>
|
||||||
<item>HighPerf</item>
|
<item>HighPerf</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="settings_service_ip_monitor">
|
<string-array name="settings_service_ip_monitor">
|
||||||
<item>@string/settings_service_ip_monitor_monitor</item>
|
<item>@string/settings_service_ip_monitor_monitor</item>
|
||||||
<item>@string/settings_service_ip_monitor_monitor_root</item>
|
<item>@string/settings_service_ip_monitor_monitor_root</item>
|
||||||
|
|||||||
@@ -86,9 +86,10 @@
|
|||||||
|
|
||||||
<string name="settings_upstream">Upstream</string>
|
<string name="settings_upstream">Upstream</string>
|
||||||
<string name="settings_downstream">Downstream</string>
|
<string name="settings_downstream">Downstream</string>
|
||||||
<string name="settings_service_masquerade">IP Masquerade</string>
|
<string name="settings_service_masquerade">IP Masquerade Mode</string>
|
||||||
<string name="settings_service_masquerade_summary">Recommended to disable this option for dummy VPNs like
|
<string name="settings_service_masquerade_none">None</string>
|
||||||
ad-blockers and socksifiers.</string>
|
<string name="settings_service_masquerade_simple">Simple</string>
|
||||||
|
<string name="settings_service_masquerade_netd">Android Netd Service</string>
|
||||||
<string name="settings_service_repeater_oc">Operating Wi\u2011Fi channel (unstable)</string>
|
<string name="settings_service_repeater_oc">Operating Wi\u2011Fi channel (unstable)</string>
|
||||||
<string name="settings_service_repeater_oc_summary">Auto (1\u201114 = 2.4GHz, 15\u2011165 = 5GHz)</string>
|
<string name="settings_service_repeater_oc_summary">Auto (1\u201114 = 2.4GHz, 15\u2011165 = 5GHz)</string>
|
||||||
<string name="settings_service_disable_ipv6">Disable IPv6 tethering</string>
|
<string name="settings_service_disable_ipv6">Disable IPv6 tethering</string>
|
||||||
|
|||||||
@@ -17,12 +17,13 @@
|
|||||||
app:icon="@drawable/ic_action_settings_input_component"
|
app:icon="@drawable/ic_action_settings_input_component"
|
||||||
app:title="@string/settings_upstream_fallback"
|
app:title="@string/settings_upstream_fallback"
|
||||||
app:summary="@string/settings_upstream_fallback_auto"/>
|
app:summary="@string/settings_upstream_fallback_auto"/>
|
||||||
<SwitchPreference
|
<com.takisoft.preferencex.SimpleMenuPreference
|
||||||
app:key="service.masquerade"
|
app:key="service.masqueradeMode"
|
||||||
app:icon="@drawable/ic_social_people"
|
app:icon="@drawable/ic_social_people"
|
||||||
app:title="@string/settings_service_masquerade"
|
app:title="@string/settings_service_masquerade"
|
||||||
app:summary="@string/settings_service_masquerade_summary"
|
app:entries="@array/settings_service_masquerade"
|
||||||
app:defaultValue="true"/>
|
app:entryValues="@array/settings_service_masquerade_values"
|
||||||
|
app:useSimpleSummaryProvider="true"/>
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
app:title="@string/settings_downstream">
|
app:title="@string/settings_downstream">
|
||||||
@@ -52,8 +53,8 @@
|
|||||||
app:entries="@array/settings_service_wifi_lock"
|
app:entries="@array/settings_service_wifi_lock"
|
||||||
app:entryValues="@array/settings_service_wifi_lock_values"
|
app:entryValues="@array/settings_service_wifi_lock_values"
|
||||||
app:defaultValue="Full"
|
app:defaultValue="Full"
|
||||||
app:summary="%s"
|
app:title="@string/settings_service_wifi_lock"
|
||||||
app:title="@string/settings_service_wifi_lock"/>
|
app:useSimpleSummaryProvider="true"/>
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
app:key="service.repeater.startOnBoot"
|
app:key="service.repeater.startOnBoot"
|
||||||
app:icon="@drawable/ic_action_autorenew"
|
app:icon="@drawable/ic_action_autorenew"
|
||||||
@@ -64,8 +65,8 @@
|
|||||||
app:entries="@array/settings_service_ip_monitor"
|
app:entries="@array/settings_service_ip_monitor"
|
||||||
app:entryValues="@array/settings_service_ip_monitor_values"
|
app:entryValues="@array/settings_service_ip_monitor_values"
|
||||||
app:defaultValue="Poll"
|
app:defaultValue="Poll"
|
||||||
app:summary="%s"
|
app:title="@string/settings_service_ip_monitor"
|
||||||
app:title="@string/settings_service_ip_monitor"/>
|
app:useSimpleSummaryProvider="true"/>
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
app:title="@string/settings_help">
|
app:title="@string/settings_help">
|
||||||
|
|||||||
Reference in New Issue
Block a user