From debbf7a4ce4c754279b4dfc4fd058dcc38de4812 Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 22 Jun 2020 10:05:12 +0800 Subject: [PATCH] Only return valid entries in light mode --- .../IpNeighbourMonitoringService.kt | 2 +- .../vpnhotspot/LocalOnlyHotspotService.kt | 2 +- .../java/be/mygod/vpnhotspot/MainActivity.kt | 2 +- .../IpNeighbourMonitoringTileService.kt | 2 +- .../be/mygod/vpnhotspot/net/IpNeighbour.kt | 63 ++++++++++--------- .../net/monitor/IpNeighbourMonitor.kt | 4 +- 6 files changed, 38 insertions(+), 37 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/IpNeighbourMonitoringService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/IpNeighbourMonitoringService.kt index f49f7608..9a25e821 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/IpNeighbourMonitoringService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/IpNeighbourMonitoringService.kt @@ -18,7 +18,7 @@ abstract class IpNeighbourMonitoringService : Service(), IpNeighbourMonitor.Call protected fun updateNotification() { val sizeLookup = neighbours.groupBy { it.dev }.mapValues { (_, neighbours) -> neighbours - .filter { it.ip is Inet4Address && it.state != IpNeighbour.State.FAILED } + .filter { it.ip is Inet4Address && it.state == IpNeighbour.State.VALID } .distinctBy { it.lladdr } .size } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt index e766e626..158c6a7c 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt @@ -129,7 +129,7 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope { override fun onIpNeighbourAvailable(neighbours: Collection) { super.onIpNeighbourAvailable(neighbours) if (Build.VERSION.SDK_INT >= 28) timeoutMonitor?.onClientsChanged(neighbours.none { - it.ip is Inet4Address && it.state != IpNeighbour.State.FAILED + it.ip is Inet4Address && it.state == IpNeighbour.State.VALID }) } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/MainActivity.kt b/mobile/src/main/java/be/mygod/vpnhotspot/MainActivity.kt index dc0c0839..f69e5175 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/MainActivity.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/MainActivity.kt @@ -32,7 +32,7 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS if (Services.p2p != null) ServiceForegroundConnector(this, model, RepeaterService::class) model.clients.observe(this) { clients -> val count = clients.count { - it.ip.any { (ip, state) -> ip is Inet4Address && state != IpNeighbour.State.FAILED } + it.ip.any { (ip, state) -> ip is Inet4Address && state == IpNeighbour.State.VALID } } if (count > 0) binding.navigation.getOrCreateBadge(R.id.navigation_clients).apply { backgroundColor = ContextCompat.getColor(this@MainActivity, R.color.colorSecondary) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/IpNeighbourMonitoringTileService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/IpNeighbourMonitoringTileService.kt index 31308799..93e966fb 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/IpNeighbourMonitoringTileService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/IpNeighbourMonitoringTileService.kt @@ -25,7 +25,7 @@ abstract class IpNeighbourMonitoringTileService : KillableTileService(), IpNeigh protected fun Tile.subtitleDevices(filter: (String) -> Boolean) { val size = neighbours - .filter { it.ip is Inet4Address && it.state != IpNeighbour.State.FAILED && filter(it.dev) } + .filter { it.ip is Inet4Address && it.state == IpNeighbour.State.VALID && filter(it.dev) } .distinctBy { it.lladdr } .size if (size > 0) subtitle(resources.getQuantityString( diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/IpNeighbour.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/IpNeighbour.kt index d32de802..04c627e0 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/IpNeighbour.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/IpNeighbour.kt @@ -11,6 +11,7 @@ import timber.log.Timber import java.io.File import java.io.FileNotFoundException import java.io.IOException +import java.net.Inet4Address import java.net.InetAddress import java.net.NetworkInterface import java.net.SocketException @@ -35,7 +36,6 @@ data class IpNeighbour(val ip: InetAddress, val dev: String, val lladdr: MacAddr * Source: https://android.googlesource.com/platform/external/iproute2/+/4b9e917/lib/ll_map.c#152 */ private val devFallback = "^if(\\d+)\$".toRegex() - private fun checkLladdrNotLoopback(lladdr: String) = if (lladdr == "00:00:00:00:00:00") "" else lladdr private fun populateList(base: IpNeighbour): List { val devParser = devFallback.matchEntire(base.dev) @@ -49,42 +49,43 @@ data class IpNeighbour(val ip: InetAddress, val dev: String, val lladdr: MacAddr } fun parse(line: String, fullMode: Boolean): List { - if (line.isBlank()) return emptyList() - return try { + return if (line.isBlank()) emptyList() else try { val match = parser.matchEntire(line)!! val ip = parseNumericAddress(match.groupValues[2]) // by regex, ip is non-empty val dev = match.groupValues[3] // by regex, dev is non-empty as well - val state = if (match.groupValues[1].isNotEmpty()) State.DELETING else - when (match.groupValues[7]) { - "", "INCOMPLETE" -> State.INCOMPLETE - "REACHABLE", "DELAY", "STALE", "PROBE", "PERMANENT" -> State.VALID - "FAILED" -> { - if (!fullMode) return populateList(IpNeighbour(ip, dev, MacAddressCompat.ALL_ZEROS_ADDRESS, - State.DELETING)) // skip parsing lladdr to avoid requesting root - State.FAILED - } - "NOARP" -> return emptyList() // skip - else -> throw IllegalArgumentException("Unknown state encountered: ${match.groupValues[7]}") - } - var lladdr = checkLladdrNotLoopback(match.groupValues[5]) - // use ARP as fallback for IPv4 - if (lladdr.isEmpty()) lladdr = checkLladdrNotLoopback(arp() - .asSequence() - .filter { parseNumericAddress(it[ARP_IP_ADDRESS]) == ip && it[ARP_DEVICE] == dev } - .map { it[ARP_HW_ADDRESS] } - .singleOrNull() ?: "") - val mac = try { - MacAddressCompat.fromString(lladdr) - } catch (e: IllegalArgumentException) { - if (match.groups[4] == null) return emptyList() - // for DELETING, we only care about IP address and do not care if MAC is not present - if (state != State.DELETING) Timber.w(IOException("Failed to find MAC address for $line", e)) - MacAddressCompat.ALL_ZEROS_ADDRESS + val state = if (match.groupValues[1].isNotEmpty()) State.DELETING else when (match.groupValues[7]) { + "", "INCOMPLETE" -> State.INCOMPLETE + "REACHABLE", "DELAY", "STALE", "PROBE", "PERMANENT" -> State.VALID + "FAILED" -> State.FAILED + "NOARP" -> return emptyList() // skip + else -> throw IllegalArgumentException("Unknown state encountered: ${match.groupValues[7]}") } - populateList(IpNeighbour(ip, dev, mac, state)) + var lladdr = MacAddressCompat.ALL_ZEROS_ADDRESS + if (!fullMode && state != State.VALID) { + // skip parsing lladdr to avoid requesting root + return populateList(IpNeighbour(ip, dev, lladdr, State.DELETING)) + } + if (match.groups[4] != null) try { + lladdr = MacAddressCompat.fromString(match.groupValues[5]) + } catch (e: IllegalArgumentException) { + Timber.w(IOException("Failed to find MAC address for $line", e)) + } + // use ARP as fallback for IPv4, except for INCOMPLETE which by definition does not have arp entry, + // or for DELETING, which we do not care about MAC not present + if (ip is Inet4Address && lladdr == MacAddressCompat.ALL_ZEROS_ADDRESS && state != State.INCOMPLETE && + state != State.DELETING) try { + lladdr = MacAddressCompat.fromString(arp() + .asSequence() + .filter { parseNumericAddress(it[ARP_IP_ADDRESS]) == ip && it[ARP_DEVICE] == dev } + .map { it[ARP_HW_ADDRESS] } + .singleOrNull() ?: throw IllegalArgumentException("singleOrNull")) + } catch (e: IllegalArgumentException) { + Timber.w(e) + } + populateList(IpNeighbour(ip, dev, lladdr, state)) } catch (e: Exception) { Timber.w(IllegalArgumentException("Unable to parse line: $line", e)) - emptyList() + emptyList() } } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/IpNeighbourMonitor.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/IpNeighbourMonitor.kt index c5102727..a6b92c08 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/IpNeighbourMonitor.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/monitor/IpNeighbourMonitor.kt @@ -16,9 +16,9 @@ class IpNeighbourMonitor private constructor() : IpMonitor() { var fullMode = false /** - * @param full Whether the failed entries should also be parsed. + * @param full Whether the invalid entries should also be parsed. * In this case it is more likely to trigger root request on API 29+. - * However, even in light mode, caller should still filter out failed entries in + * However, even in light mode, caller should still filter out invalid entries in * [Callback.onIpNeighbourAvailable] in case the full mode was requested by other callers. */ fun registerCallback(callback: Callback, full: Boolean = false) = synchronized(callbacks) {