Commit 476a6257 authored by Mygod's avatar Mygod

Speed up subnet matching using binary search

parent 1421bb70
......@@ -40,15 +40,22 @@ class AclMatcher {
}, {
if (it.startsWith("(?:^|\\.)googleapis")) proxyDomains.add(it.toRegex())
})
subnetsIpv4 = subnets.filter { it.address is Inet4Address }.map { it.toImmutable() }
subnetsIpv6 = subnets.filter { it.address is Inet6Address }.map { it.toImmutable() }
subnetsIpv4 = subnets.asSequence().filter { it.address is Inet4Address }.map { it.toImmutable() }
.sortedWith(Subnet.Immutable).toList()
subnetsIpv6 = subnets.asSequence().filter { it.address is Inet6Address }.map { it.toImmutable() }
.sortedWith(Subnet.Immutable).toList()
this.bypass = bypass
}
Log.d("AclMatcher", "ACL initialized in $time ns")
}
fun shouldBypassIpv4(ip: ByteArray) = bypass xor subnetsIpv4.any { it.matches(ip) }
fun shouldBypassIpv6(ip: ByteArray) = bypass xor subnetsIpv6.any { it.matches(ip) }
private fun quickMatches(subnets: List<Subnet.Immutable>, ip: ByteArray): Boolean {
val i = subnets.binarySearch(Subnet.Immutable(ip), Subnet.Immutable)
return i >= 0 || i < -1 && subnets[-i - 2].matches(ip)
}
fun shouldBypassIpv4(ip: ByteArray) = bypass xor quickMatches(subnetsIpv4, ip)
fun shouldBypassIpv6(ip: ByteArray) = bypass xor quickMatches(subnetsIpv6, ip)
fun shouldBypass(host: String): Boolean? {
if (bypassDomains.any { it.matches(host) }) return true
if (proxyDomains.any { it.matches(host) }) return false
......
......@@ -45,7 +45,17 @@ class Subnet(val address: InetAddress, val prefixSize: Int) : Comparable<Subnet>
require(prefixSize in 0..addressLength) { "prefixSize $prefixSize not in 0..$addressLength" }
}
class Immutable(private val a: ByteArray, private val prefixSize: Int) {
class Immutable(private val a: ByteArray, private val prefixSize: Int = 0) {
companion object : Comparator<Immutable> {
override fun compare(a: Immutable, b: Immutable): Int {
check(a.a.size == b.a.size)
for (i in a.a.indices) {
val result = a.a[i].compareTo(b.a[i])
if (result != 0) return result
}
return 0
}
}
fun matches(b: ByteArray): Boolean {
if (a.size != b.size) return false
var i = 0
......@@ -53,12 +63,17 @@ class Subnet(val address: InetAddress, val prefixSize: Int) : Comparable<Subnet>
if (a[i] != b[i]) return false
++i
}
if (i * 8 == prefixSize) return true
val mask = 256 - (1 shl i * 8 + 8 - prefixSize)
return a[i].toInt() and mask == b[i].toInt() and mask
return i * 8 == prefixSize || a[i].toInt() == b[i].toInt() and 256 - (1 shl i * 8 + 8 - prefixSize)
}
}
fun toImmutable() = Immutable(address.address, prefixSize)
fun toImmutable() = Immutable(address.address.also {
var i = prefixSize / 8
if (prefixSize % 8 > 0) {
it[i] = (it[i].toInt() and 256 - (1 shl i * 8 + 8 - prefixSize)).toByte()
++i
}
while (i < it.size) it[i++] = 0
}, prefixSize)
override fun toString(): String =
if (prefixSize == addressLength) address.hostAddress else address.hostAddress + '/' + prefixSize
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment