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:
Mygod
2018-01-04 14:56:07 +08:00
parent 19feec30bc
commit faf5466ced
6 changed files with 52 additions and 51 deletions

View File

@@ -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"

View File

@@ -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])
}
}
}
}

View File

@@ -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)

View File

@@ -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
} }

View 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] })
}
}

View File

@@ -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"