Auto detect prefix length as well
Unfortunately NetworkInterface requires INTERNET permission. (this can actually be bypassed very easily but let's prefer usage of public API)
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
|
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".App"
|
android:name=".App"
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
package be.mygod.vpnhotspot
|
|
||||||
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class ArpCache(downstream: String? = null) : HashMap<String, String>() {
|
|
||||||
companion object {
|
|
||||||
private val spaces = " +".toPattern()
|
|
||||||
private val mac = "^([0-9a-f]{2}:){5}[0-9a-f]{2}$".toPattern()
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
File("/proc/net/arp").bufferedReader().useLines {
|
|
||||||
for (line in it) {
|
|
||||||
val parts = line.split(spaces)
|
|
||||||
// IP address HW type Flags HW address Mask Device
|
|
||||||
if (parts.size >= 4 && (downstream == null || parts.getOrNull(5) == downstream) &&
|
|
||||||
mac.matcher(parts[3]).matches()) put(parts[3], parts[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -16,6 +16,7 @@ import android.support.v4.content.LocalBroadcastManager
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import be.mygod.vpnhotspot.App.Companion.app
|
import be.mygod.vpnhotspot.App.Companion.app
|
||||||
|
import java.net.NetworkInterface
|
||||||
|
|
||||||
class HotspotService : Service(), WifiP2pManager.ChannelListener {
|
class HotspotService : Service(), WifiP2pManager.ChannelListener {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -63,26 +64,7 @@ class HotspotService : Service(), WifiP2pManager.ChannelListener {
|
|||||||
val info = intent.getParcelableExtra<WifiP2pInfo>(WifiP2pManager.EXTRA_WIFI_P2P_INFO)
|
val info = intent.getParcelableExtra<WifiP2pInfo>(WifiP2pManager.EXTRA_WIFI_P2P_INFO)
|
||||||
val net = intent.getParcelableExtra<NetworkInfo>(WifiP2pManager.EXTRA_NETWORK_INFO)
|
val net = intent.getParcelableExtra<NetworkInfo>(WifiP2pManager.EXTRA_NETWORK_INFO)
|
||||||
val group = intent.getParcelableExtra<WifiP2pGroup>(WifiP2pManager.EXTRA_WIFI_P2P_GROUP)
|
val group = intent.getParcelableExtra<WifiP2pGroup>(WifiP2pManager.EXTRA_WIFI_P2P_GROUP)
|
||||||
val hostAddress = info.groupOwnerAddress?.hostAddress
|
if (downstream == null) onGroupCreated(info, group)
|
||||||
val downstream = group.`interface`
|
|
||||||
if (info.groupFormed && info.isGroupOwner &&
|
|
||||||
downstream != null && hostAddress != null && this.downstream == null) {
|
|
||||||
this.downstream = downstream
|
|
||||||
this.hostAddress = hostAddress
|
|
||||||
if (noisySu("echo 1 >/proc/sys/net/ipv4/ip_forward",
|
|
||||||
"ip route add default dev $upstream scope link table 62",
|
|
||||||
"ip route add $hostAddress/$subnetPrefixLength dev $downstream scope link table 62",
|
|
||||||
"ip route add broadcast 255.255.255.255 dev $downstream scope link table 62",
|
|
||||||
"ip rule add from $hostAddress/$subnetPrefixLength lookup 62",
|
|
||||||
"iptables -N vpnhotspot_fwd",
|
|
||||||
"iptables -A vpnhotspot_fwd -i $upstream -o $downstream -m state --state ESTABLISHED,RELATED -j ACCEPT",
|
|
||||||
"iptables -A vpnhotspot_fwd -i $downstream -o $upstream -j ACCEPT",
|
|
||||||
"iptables -I FORWARD -j vpnhotspot_fwd",
|
|
||||||
"iptables -t nat -A PREROUTING -i $downstream -p tcp -d $hostAddress --dport 53 -j DNAT --to-destination $dns",
|
|
||||||
"iptables -t nat -A PREROUTING -i $downstream -p udp -d $hostAddress --dport 53 -j DNAT --to-destination $dns")) {
|
|
||||||
doStart(group)
|
|
||||||
} else startFailure("Something went wrong, please check logcat.")
|
|
||||||
}
|
|
||||||
this@HotspotService.group = group
|
this@HotspotService.group = group
|
||||||
binder.data?.onGroupChanged()
|
binder.data?.onGroupChanged()
|
||||||
showNotification(group)
|
showNotification(group)
|
||||||
@@ -99,7 +81,7 @@ class HotspotService : Service(), WifiP2pManager.ChannelListener {
|
|||||||
* https://android.googlesource.com/platform/frameworks/base/+/android-4.0.1_r1/wifi/java/android/net/wifi/p2p/WifiP2pService.java#1028
|
* https://android.googlesource.com/platform/frameworks/base/+/android-4.0.1_r1/wifi/java/android/net/wifi/p2p/WifiP2pService.java#1028
|
||||||
* https://android.googlesource.com/platform/frameworks/opt/net/wifi/+/a8d5e40/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java#2547
|
* https://android.googlesource.com/platform/frameworks/opt/net/wifi/+/a8d5e40/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java#2547
|
||||||
*/
|
*/
|
||||||
private val subnetPrefixLength get() = app.pref.getString("service.subnetPrefixLength", "24")
|
private var subnetPrefixLength: Short = 24
|
||||||
private val dns get() = app.pref.getString("service.dns", "8.8.8.8:53")
|
private val dns get() = app.pref.getString("service.dns", "8.8.8.8:53")
|
||||||
|
|
||||||
var status = Status.IDLE
|
var status = Status.IDLE
|
||||||
@@ -183,6 +165,35 @@ class HotspotService : Service(), WifiP2pManager.ChannelListener {
|
|||||||
Intent(this, MainActivity::class.java), PendingIntent.FLAG_UPDATE_CURRENT))
|
Intent(this, MainActivity::class.java), PendingIntent.FLAG_UPDATE_CURRENT))
|
||||||
.build())
|
.build())
|
||||||
|
|
||||||
|
private fun onGroupCreated(info: WifiP2pInfo, group: WifiP2pGroup) {
|
||||||
|
val owner = info.groupOwnerAddress
|
||||||
|
val hostAddress = owner?.hostAddress
|
||||||
|
val downstream = group.`interface`
|
||||||
|
if (!info.groupFormed || !info.isGroupOwner || downstream == null || hostAddress == null) return
|
||||||
|
this.downstream = downstream
|
||||||
|
this.hostAddress = hostAddress
|
||||||
|
var subnetPrefixLength = NetworkInterface.getByName(downstream)?.interfaceAddresses
|
||||||
|
?.singleOrNull { it.address == owner }?.networkPrefixLength
|
||||||
|
if (subnetPrefixLength == null) {
|
||||||
|
Log.w(TAG, "Unable to find prefix length of interface $downstream, 24 is assumed")
|
||||||
|
subnetPrefixLength = 24
|
||||||
|
}
|
||||||
|
this.subnetPrefixLength = subnetPrefixLength
|
||||||
|
if (noisySu("echo 1 >/proc/sys/net/ipv4/ip_forward",
|
||||||
|
"ip route add default dev $upstream scope link table 62",
|
||||||
|
"ip route add $hostAddress/$subnetPrefixLength dev $downstream scope link table 62",
|
||||||
|
"ip route add broadcast 255.255.255.255 dev $downstream scope link table 62",
|
||||||
|
"ip rule add from $hostAddress/$subnetPrefixLength lookup 62",
|
||||||
|
"iptables -N vpnhotspot_fwd",
|
||||||
|
"iptables -A vpnhotspot_fwd -i $upstream -o $downstream -m state --state ESTABLISHED,RELATED -j ACCEPT",
|
||||||
|
"iptables -A vpnhotspot_fwd -i $downstream -o $upstream -j ACCEPT",
|
||||||
|
"iptables -I FORWARD -j vpnhotspot_fwd",
|
||||||
|
"iptables -t nat -A PREROUTING -i $downstream -p tcp -d $hostAddress --dport 53 -j DNAT --to-destination $dns",
|
||||||
|
"iptables -t nat -A PREROUTING -i $downstream -p udp -d $hostAddress --dport 53 -j DNAT --to-destination $dns")) {
|
||||||
|
doStart(group)
|
||||||
|
} else startFailure("Something went wrong, please check logcat.")
|
||||||
|
}
|
||||||
|
|
||||||
private fun clean() {
|
private fun clean() {
|
||||||
if (receiverRegistered) {
|
if (receiverRegistered) {
|
||||||
unregisterReceiver(receiver)
|
unregisterReceiver(receiver)
|
||||||
|
|||||||
@@ -66,14 +66,14 @@ class MainActivity : AppCompatActivity(), ServiceConnection, Toolbar.OnMenuItemC
|
|||||||
inner class ClientAdapter : RecyclerView.Adapter<ClientViewHolder>() {
|
inner class ClientAdapter : RecyclerView.Adapter<ClientViewHolder>() {
|
||||||
private var owner: WifiP2pDevice? = null
|
private var owner: WifiP2pDevice? = null
|
||||||
private lateinit var clients: MutableCollection<WifiP2pDevice>
|
private lateinit var clients: MutableCollection<WifiP2pDevice>
|
||||||
private lateinit var arpCache: ArpCache
|
private lateinit var arpCache: Map<String, String>
|
||||||
|
|
||||||
fun fetchClients() {
|
fun fetchClients() {
|
||||||
if (data.running) {
|
if (data.running) {
|
||||||
val binder = binder!!
|
val binder = binder!!
|
||||||
owner = binder.service.group.owner
|
owner = binder.service.group.owner
|
||||||
clients = binder.service.group.clientList
|
clients = binder.service.group.clientList
|
||||||
arpCache = ArpCache(binder.service.downstream)
|
arpCache = NetUtils.arp(binder.service.downstream)
|
||||||
} else owner = null
|
} else owner = null
|
||||||
notifyDataSetChanged() // recreate everything
|
notifyDataSetChanged() // recreate everything
|
||||||
}
|
}
|
||||||
|
|||||||
17
mobile/src/main/java/be/mygod/vpnhotspot/NetUtils.kt
Normal file
17
mobile/src/main/java/be/mygod/vpnhotspot/NetUtils.kt
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package be.mygod.vpnhotspot
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
object NetUtils {
|
||||||
|
private val spaces = " +".toPattern()
|
||||||
|
private val mac = "^([0-9a-f]{2}:){5}[0-9a-f]{2}$".toPattern()
|
||||||
|
|
||||||
|
fun arp(iface: String? = null) = File("/proc/net/arp").bufferedReader().useLines {
|
||||||
|
// IP address HW type Flags HW address Mask Device
|
||||||
|
it.map { it.split(spaces) }
|
||||||
|
.drop(1)
|
||||||
|
.filter { it.size >= 4 && (iface == null || it.getOrNull(5) == iface) &&
|
||||||
|
mac.matcher(it[3]).matches() }
|
||||||
|
.associateBy({ it[3] }, { it[0] })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,13 +8,6 @@
|
|||||||
android:title="Upstream interface"
|
android:title="Upstream interface"
|
||||||
android:summary="%s"
|
android:summary="%s"
|
||||||
android:defaultValue="tun0"/>
|
android:defaultValue="tun0"/>
|
||||||
<AutoSummaryEditTextPreference
|
|
||||||
android:key="service.subnetPrefixLength"
|
|
||||||
android:title="Subnet prefix length"
|
|
||||||
android:summary="%s"
|
|
||||||
android:inputType="number"
|
|
||||||
android:maxLength="3"
|
|
||||||
android:defaultValue="24"/>
|
|
||||||
<AutoSummaryEditTextPreference
|
<AutoSummaryEditTextPreference
|
||||||
android:key="service.dns"
|
android:key="service.dns"
|
||||||
android:title="Downstream DNS server:port"
|
android:title="Downstream DNS server:port"
|
||||||
|
|||||||
Reference in New Issue
Block a user