diff --git a/src/main/kotlin/CdirAddress.kt b/src/main/kotlin/CdirAddress.kt new file mode 100644 index 0000000..07c965c --- /dev/null +++ b/src/main/kotlin/CdirAddress.kt @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.net.InetAddress +import java.net.UnknownHostException +import kotlin.math.floor +import kotlin.math.ln +import kotlin.math.pow + + +inline class CdirAddress(val cdir: String) { + override fun toString(): String { + return "$cdir (${this.startAddress()?.hostAddress}...${this.endAddress()?.hostAddress})" + } +} + +internal fun CdirAddress.addressCount(): Long { + return inet2long(endAddress()) - inet2long(startAddress()) + 1 +} + +private fun CdirAddress.inetAddress(): InetAddress { + return InetAddress.getByName(cdir.substringBefore("/")) +} + +internal fun CdirAddress.prefix(): Int { + return cdir.substringAfter("/").toInt() +} + +internal fun CdirAddress.startAddress(): InetAddress? { + return long2inet(inet2long(this.inetAddress()) and prefix2mask(this.prefix())) +} + +internal fun CdirAddress.endAddress(): InetAddress? { + return long2inet((inet2long(this.inetAddress()) and prefix2mask(this.prefix())) + (1L shl(32 - this.prefix())) - 1) +} + +internal fun CdirAddress.merge(other: CdirAddress): List { + val end = this.endAddress()!!.toLong().coerceAtLeast(other.endAddress()!!.toLong()).toInetAddress()!! + return toCdirAddresses(this.startAddress()!!, end) +} + +internal fun InetAddress.plus(value: Int): InetAddress? { + return long2inet(inet2long(this) + value) +} + +internal fun InetAddress.minus(value: Int): InetAddress? { + return long2inet(inet2long(this) - value) +} + +private fun prefix2mask(bits: Int): Long { + return -0x100000000L shr bits and 0xFFFFFFFFL +} + +internal fun InetAddress.toLong(): Long { + return inet2long(this) +} + +internal fun Long.toInetAddress(): InetAddress? { + return long2inet(this) +} + +private fun inet2long(addr: InetAddress?): Long { + var result: Long = 0 + if (addr != null) { + for (b in addr.address) { + result = (result shl 8) or (b.toLong() and 0xFF) + } + } + return result +} + + +fun toCdirAddresses(start: InetAddress, end: InetAddress): List { + val listResult: MutableList = ArrayList() +// echo("toCdirAddress(" + start.hostAddress + "," + end.hostAddress + ")") + + var from = inet2long(start) + val to = inet2long(end) + + while (to >= from) { + var prefix: Byte = 32 + while (prefix > 0) { + val mask = prefix2mask(prefix - 1) + if (from and mask != from) break + prefix-- + } + val max = (32 - floor(ln((to - from + 1).toDouble()) / ln(2.0))).toByte() + if (prefix < max) prefix = max + listResult.add(CdirAddress("${long2inet(from)?.hostAddress}/$prefix")) + from += 2.0.pow((32 - prefix).toDouble()).toLong() + } + + return listResult +} + +@Suppress("NAME_SHADOWING") +private fun long2inet(addr: Long): InetAddress? { + var addr = addr + return try { + val b = ByteArray(4) + for (i in b.indices.reversed()) { + b[i] = (addr and 0xFF).toByte() + addr = addr shr 8 + } + InetAddress.getByAddress(b) + } catch (ignore: UnknownHostException) { + null + } +} diff --git a/src/main/kotlin/Subnet.kt b/src/main/kotlin/Subnet.kt deleted file mode 100644 index 51f9934..0000000 --- a/src/main/kotlin/Subnet.kt +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import Subnet.Companion.toInteger -import java.util.regex.Matcher -import java.util.regex.Pattern - -inline class Address(val value: String) - -internal fun Address.asInteger(): Int { - return toInteger(this) -} - -class Subnet { - private var netmask = 0 - private var address = 0 - private var network = 0 - private var broadcast = 0 - - /** Whether the broadcast/network address are included in host count */ - var isInclusiveHostCount = true - - /** - * Constructor that takes a CIDR-notation string, e.g. "192.168.0.1/16" - * @param cidrNotation A CIDR-notation string, e.g. "192.168.0.1/16" - * @throws IllegalArgumentException if the parameter is invalid, - * i.e. does not match n.n.n.n/m where n=1-3 decimal digits, m = 1-3 decimal digits in range 1-32 - */ - constructor(cidrNotation: String) { - calculate(cidrNotation) - } - - /** - * Constructor that takes a dotted decimal address and a dotted decimal mask. - * @param address An IP address, e.g. "192.168.0.1" - * @param mask A dotted decimal netmask e.g. "255.255.0.0" - * @throws IllegalArgumentException if the address or mask is invalid, - * i.e. does not match n.n.n.n where n=1-3 decimal digits and the mask is not all zeros - */ - constructor(address: String, mask: String) { - calculate(toCidrNotation(Address(address), Address(mask))) - } - - override fun toString(): String { - return info.toString() - } - - /** - * Convenience container for subnet summary information. - * - */ - inner class SubnetInfo internal constructor() { - // long versions of the values (as unsigned int) which are more suitable for range checking - private fun networkLong(): Long { - return (network and UNSIGNED_INT_MASK.toInt()).toLong() - } - - private fun broadcastLong(): Long { - return (broadcast and UNSIGNED_INT_MASK.toInt()).toLong() - } - - private fun low(): Int { - return if (isInclusiveHostCount) network else if (broadcastLong() - networkLong() > 1) network + 1 else 0 - } - - private fun high(): Int { - return if (isInclusiveHostCount) broadcast else if (broadcastLong() - networkLong() > 1) broadcast - 1 else 0 - } - - /** - * Returns true if the parameter `address` is in the - * range of usable endpoint addresses for this subnet. This excludes the - * network and broadcast adresses. - * @param address A dot-delimited IPv4 address, e.g. "192.168.0.1" - * @return True if in range, false otherwise - */ - fun isInRange(address: Address): Boolean { - return isInRange(toInteger(address)) - } - - fun overlaps(other: Subnet.SubnetInfo): Boolean { - return this.lowAddress.normalizeAddress() <= other.highAddress.normalizeAddress() && other.lowAddress.normalizeAddress() <= this.highAddress.normalizeAddress() - } - - /** - * - * @param address the address to check - * @return true if it is in range - * @since 3.4 (made public) - */ - fun isInRange(address: Int): Boolean { - val addLong = (address and UNSIGNED_INT_MASK.toInt()).toLong() - val lowLong = (low() and UNSIGNED_INT_MASK.toInt()).toLong() - val highLong = (high() and UNSIGNED_INT_MASK.toInt()).toLong() - return addLong in lowLong..highLong - } - - val broadcastAddress: Address - get() = format(toArray(broadcast)) - - val networkAddress: Address - get() = format(toArray(network)) - - fun getNetmask(): Address { - return format(toArray(netmask)) - } - - fun getAddress(): Address { - return format(toArray(address)) - } - - /** - * Return the low address as a dotted IP address. - * Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false. - * - * @return the IP address in dotted format, may be "0.0.0.0" if there is no valid address - */ - val lowAddress: Address - get() = format(toArray(low())) - - /** - * Return the high address as a dotted IP address. - * Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false. - * - * @return the IP address in dotted format, may be "0.0.0.0" if there is no valid address - */ - val highAddress: Address - get() = format(toArray(high())) - - val cidrPrefix: String - get() = cidrSignature.split("/")[1] - - val cidrAddress: String - get() = cidrSignature.split("/")[0] - - /** - * Get the count of available addresses. - * Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false. - * @return the count of addresses, may be zero. - * @throws RuntimeException if the correct count is greater than `Integer.MAX_VALUE` - */ - @get:Deprecated("(3.4) use {@link #getAddressCountLong()} instead") - val addressCount: Int - get() { - val countLong = getAddressCountLong() - if (countLong > Int.MAX_VALUE) { - throw RuntimeException("Count is larger than an integer: $countLong") - } - // N.B. cannot be negative - return countLong.toInt() - } - - /** - * Get the count of available addresses. - * Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false. - * @return the count of addresses, may be zero. - * @since 3.4 - */ - fun getAddressCountLong(): Long { - val b = broadcastLong() - val n = networkLong() - val count = b - n + if (isInclusiveHostCount) 1 else -1 - return if (count < 0) 0 else count - } - - fun toArray(): IntArray { - return toArray(asInteger(getAddress())) - } - - val cidrSignature: String - get() { - return toCidrNotation( - format(toArray(address)), - format(toArray(netmask)) - ) - } - - val allAddresses: Array - get() { - val ct = addressCount - val addresses = arrayOfNulls
(ct) - if (ct == 0) { - return addresses - } - var add = low() - var j = 0 - while (add <= high()) { - addresses[j] = format(toArray(add)) - ++add - ++j - } - return addresses - } - - /** - * {@inheritDoc} - * @since 2.2 - */ - override fun toString(): String { - val buf = StringBuilder() - buf.append("CIDR Signature:\t[").append(cidrSignature).append("]") - .append(" Netmask: [").append(getNetmask()).append("]\n") - .append("Network:\t[").append(networkAddress).append("]\n") - .append("Broadcast:\t[").append(broadcastAddress).append("]\n") - .append("First Address:\t[").append(lowAddress).append("]\n") - .append("Last Address:\t[").append(highAddress).append("]\n") - .append("# Addresses:\t[").append(addressCount).append("]\n") - return buf.toString() - } - } - - /** - * Return a [SubnetInfo] instance that contains subnet-specific statistics - * @return new instance - */ - val info: SubnetInfo - get() = SubnetInfo() - - /* - * Initialize the internal fields from the supplied CIDR mask - */ - private fun calculate(mask: String) { - val matcher = cidrPattern.matcher(mask) - if (matcher.matches()) { - address = matchAddress(matcher) - - /* Create a binary netmask from the number of bits specification /x */ - val cidrPart = rangeCheck(matcher.group(5).toInt(), 0, NBITS) - for (j in 0 until cidrPart) { - netmask = netmask or (1 shl 31 - j) - } - - /* Calculate base network address */network = address and netmask - - /* Calculate broadcast address */broadcast = network or netmask.inv() - } else { - throw IllegalArgumentException("Could not parse [$mask]") - } - } - - /* - * Count the number of 1-bits in a 32-bit integer using a divide-and-conquer strategy - * see Hacker's Delight section 5.1 - */ - private fun pop(x: Int): Int { - var x = x - x = x - (x ushr 1 and 0x55555555) - x = (x and 0x33333333) + (x ushr 2 and 0x33333333) - x = x + (x ushr 4) and 0x0F0F0F0F - x = x + (x ushr 8) - x = x + (x ushr 16) - return x and 0x0000003F - } - - /* Convert two dotted decimal addresses to a single xxx.xxx.xxx.xxx/yy format - * by counting the 1-bit population in the mask address. (It may be better to count - * NBITS-#trailing zeroes for this case) - */ - private fun toCidrNotation(addr: Address, mask: Address): String { - return addr.value + "/" + pop(toInteger(mask)) - } - - companion object { - private const val IP_ADDRESS = "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})" - private const val SLASH_FORMAT = IP_ADDRESS + "/(\\d{1,3})" - private val addressPattern = Pattern.compile(IP_ADDRESS) - private val cidrPattern = Pattern.compile(SLASH_FORMAT) - private const val NBITS = 32 - /* Mask to convert unsigned int to a long (i.e. keep 32 bits) */ - private const val UNSIGNED_INT_MASK = 0x0FFFFFFFFL - - - internal fun addressFromInteger(address: Int): Address { - return format(toArray(address)) - } - - /** - * Convert a packed integer address into a 4-element array - */ - private fun toArray(`val`: Int): IntArray { - val ret = IntArray(4) - for (j in 3 downTo 0) { - ret[j] = ret[j] or (`val` ushr 8 * (3 - j) and 0xff) - } - return ret - } - - /** - * Convert a 4-element array into dotted decimal format - */ - private fun format(octets: IntArray): Address { - val str = StringBuilder() - for (i in octets.indices) { - str.append(octets[i]) - if (i != octets.size - 1) { - str.append(".") - } - } - return Address(str.toString()) - } - - private fun asInteger(address: Address): Int { - return toInteger(address) - } - - /* - * Convenience function to check integer boundaries. - * Checks if a value x is in the range [begin,end]. - * Returns x if it is in range, throws an exception otherwise. - */ - private fun rangeCheck(value: Int, begin: Int, end: Int): Int { - if (value in begin..end) { // (begin,end] - return value - } - throw IllegalArgumentException("Value [$value] not in range [$begin,$end]") - } - - /* - * Convenience method to extract the components of a dotted decimal address and - * pack into an integer using a regex match - */ - private fun matchAddress(matcher: Matcher): Int { - var addr = 0 - for (i in 1..4) { - val n = rangeCheck(matcher.group(i).toInt(), 0, 255) - addr = addr or (n and 0xff shl 8 * (4 - i)) - } - return addr - } - - /* - * Convert a dotted decimal format address to a packed integer format - */ - internal fun toInteger(address: Address): Int { - val matcher = addressPattern.matcher(address.value) - return if (matcher.matches()) { - matchAddress(matcher) - } else { - throw IllegalArgumentException("Could not parse [$address]") - } - } - } -} diff --git a/src/main/kotlin/beeline.kt b/src/main/kotlin/beeline.kt index 8f458f3..965410a 100644 --- a/src/main/kotlin/beeline.kt +++ b/src/main/kotlin/beeline.kt @@ -1,4 +1,3 @@ -import Subnet.Companion.addressFromInteger import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.core.NoOpCliktCommand import com.github.ajalt.clikt.core.context @@ -9,7 +8,8 @@ import com.github.ajalt.mordant.rendering.TextColors import com.github.ajalt.mordant.rendering.TextStyles import com.jakewharton.picnic.TextAlignment import com.jakewharton.picnic.table -import kotlin.math.log2 +import java.net.InetAddress +import kotlin.math.max private inline fun assert(value: Boolean, lazyMessage: () -> Any) { if (!value) { @@ -18,7 +18,13 @@ private inline fun assert(value: Boolean, lazyMessage: () -> Any) { } } -data class Beeline(val route: Subnet, val isGap: Boolean = false) +sealed class Route(val route: CdirAddress) { + override fun toString(): String { + return route.toString() + } + class IncludedRoute(r: CdirAddress) : Route(r) + class ExcludedRoute(r: CdirAddress) : Route(r) +} class ColorHelpFormatter : CliktHelpFormatter() { override fun renderTag(tag: String, value: String) = TextColors.green(super.renderTag(tag, value)) @@ -51,100 +57,104 @@ class BeelineCommand: CliktCommand(help = """ private val showGaps by option(help="Show gaps in table").flag() override fun run() { - val subnets = input.map { Subnet("${it.split("/")[0].trim()}/${it.split("/")[1].trim()}").apply { isInclusiveHostCount = true } }.toList() + val excludedAddresses = input + .map { CdirAddress(it) } + .sortedBy { it.startAddress()?.toLong() } + .let { mergeIntervals(it) } + .also { echo("$it") } + + val routes: List = calculateRoutes(excludedAddresses) + .map { Route.IncludedRoute(it) } + .toMutableList() + .apply { + addAll(excludedAddresses.map { Route.ExcludedRoute(it) }) + } + .sortedBy { it.route.startAddress()?.toLong() } + .also { verifyRoutes(it) } - val routes = calculateSubnetRoutes(subnets) - val finalRoutes = mergeRoutes(subnets, routes) - val gaps = if (showGaps) calculateGaps(finalRoutes) else listOf() + outputRoutes(routes, format) + } - val beelines = finalRoutes.map { Beeline(it) } - val beelineGaps = gaps.map { Beeline(it, true) } + private fun mergeIntervals(routes: List): List { + if (routes.isEmpty()) return routes - // merge routes and gaps - val result = beelines.toMutableList().apply { - addAll(beelineGaps) - sortBy { it.route.info.lowAddress.normalizeAddress() } - }.toList().also { verifyRoutes(it) } + val routes = routes.sortedBy { it.startAddress()?.toLong() } - outputRoutes(result, format) - } + val first = routes.first() + var start = first.startAddress()!!.toLong() + var end = first.endAddress()!!.toLong() - private fun verifyRoutes(routes: List) { - val routeAddrCount = routes.map { it.route.info.getAddressCountLong() }.sum() + val result = mutableListOf() + routes.asSequence().drop(1).forEach { current -> + if (current.startAddress()!!.toLong() <= end) { + end = max(current.endAddress()!!.toLong(), end) + } else { + result.addAll(toCdirAddresses(start.toInetAddress()!!, end.toInetAddress()!!)) + start = current.startAddress()!!.toLong() + end = current.endAddress()!!.toLong() + } + } - assert((routeAddrCount - UInt.MAX_VALUE.toLong() - 1) == 0L) { "Invalid routes $routeAddrCount != ${UInt.MAX_VALUE.toLong() - 1}" } + result.addAll(toCdirAddresses(start.toInetAddress()!!, end.toInetAddress()!!)) + return result } + private fun calculateRoutes(excludedAddresses: List): List { + val routes = mutableListOf() - private fun calculateSubnetRoutes(exclusions: List): List { - val routes: MutableMap = mutableMapOf() - - exclusions.forEach { util -> - val subnet = util.info.toArray() - echo("Calculating routes for ${util.info.cidrSignature}") - val baseMask = 0b10000000 + var start = InetAddress.getByName("0.0.0.0") + excludedAddresses.sortedBy { it.startAddress()?.toLong() }.forEach { exclude -> +// echo("Exclude ${exclude.startAddress()?.hostAddress} ... ${exclude.endAddress()?.hostAddress}") - for (p in 0 until util.info.cidrPrefix.toInt()) { - val bitIndex = p / 8 - val prefix = p % 8 - val flipPos = (baseMask shr prefix) and 0xFF - val flipMask = createBitFlipMask(prefix) + toCdirAddresses(start, exclude.startAddress()?.minus(1)!!).forEach { include -> + routes.add(include) + } + start = exclude.endAddress()?.plus(1) + } + val end = InetAddress.getByName("255.255.255.255") - val result = (subnet[bitIndex] xor flipPos) and flipMask - val route = formatRoute(subnet, bitIndex, result, p + 1) + // if the 'start' address is 0.0.0.0 it means we have a complete set already + if (start.toLong().toUInt() == end.toLong().toUInt() + 1u) return routes - // check if route address already exist, if it does, take the most restrictive one, aka. the one with bigger yy - val yy = route.info.cidrPrefix.toInt() - val hit = routes[route.info.getAddress()] - if (hit == null || yy > hit) { - routes[route.info.getAddress()] = yy - } - } + toCdirAddresses(start, end).forEach { include -> + routes.add(include) } - return routes.toSubnet() + return routes } - private fun calculateGaps(routes: List): List { - val gaps = mutableListOf() - routes.addZeroZeroZeroZero().map { it.info }.zipWithNext().forEach { - findGaps(it.first, it.second)?.let { _ -> - val mask = (it.first.highAddress.asInteger() + 1) xor (it.second.lowAddress.asInteger() - 1) - val prefix = if (mask == 0) 0 else log2(mask.toDouble()) + 1 - val address = addressFromInteger(it.first.highAddress.asInteger() + 1) - gaps.add(Subnet("${address.value}/${32-prefix.toInt()}")) - } - } + private fun verifyRoutes(routes: List) { + val routeAddrCount: Long = routes.sumOf { it.route.addressCount() } - return gaps + assert((routeAddrCount - UInt.MAX_VALUE.toLong() - 1) == 0L) { "Invalid routes $routeAddrCount != ${UInt.MAX_VALUE.toLong() - 1}" } } - private fun outputRoutes(finalRoutes: List, format: String?) { + private fun outputRoutes(finalRoutes: List, format: String?) { if (format == null) outputTableRoutes(finalRoutes) else outputCodeRoutes(finalRoutes, format) } - private fun outputCodeRoutes(routes: List, format: String) { + private fun outputCodeRoutes(routes: List, format: String) { println() routes.forEach { - if (it.isGap) { + if (it is Route.ExcludedRoute) { println(""" - // Excluded range: ${it.route.info.lowAddress} -> ${it.route.info.highAddress} + // Excluded range: ${it.route.startAddress()?.hostAddress} -> ${it.route.endAddress()?.hostAddress} """.trimIndent()) } else { - val templated = format.replace("%cidr", it.route.info.cidrAddress) - .replace("%prefix", it.route.info.cidrPrefix) - .replace("%lowAddr", it.route.info.lowAddress.value) - .replace("%highAddr", it.route.info.highAddress.value) + val templated = format.replace("%cidr", it.route.cdir) + .replace("%prefix", it.route.prefix().toString()) + .replace("%lowAddr", it.route.startAddress()!!.hostAddress) + .replace("%highAddr", it.route.endAddress()!!.hostAddress) println(templated) } } } - private fun outputTableRoutes(routes: List) { + private fun outputTableRoutes(routes: List) { println() println( @@ -158,51 +168,20 @@ class BeelineCommand: CliktCommand(help = """ row(TextStyles.bold("CIDR"), TextStyles.bold("Low Address"), TextStyles.bold("High Address")) routes.forEach { - if (it.isGap) { + if (it is Route.ExcludedRoute) { row { - cell(TextColors.red("${it.route.info.lowAddress.value} -> ${it.route.info.highAddress.value}")) { + cell(TextColors.red("${it.route.startAddress()?.hostAddress} -> ${it.route.endAddress()?.hostAddress}")) { alignment = TextAlignment.MiddleCenter columnSpan = 3 } } } else { - row(it.route.info.cidrSignature, it.route.info.lowAddress.value, it.route.info.highAddress.value) + row(it.route.cdir, it.route.startAddress()?.hostAddress, it.route.endAddress()?.hostAddress) } } } ) } - - private fun findGaps(first: Subnet.SubnetInfo, second: Subnet.SubnetInfo): Pair? { - val highestFromCurrentRoute = first.highAddress.asInteger() - val expectedNextAddress = highestFromCurrentRoute + 1 - val lowestFromNextRoute = second.lowAddress.asInteger() - - if (lowestFromNextRoute != expectedNextAddress) { - return addressFromInteger(expectedNextAddress) to addressFromInteger(lowestFromNextRoute - 1) - } - - return null - } - - private fun mergeRoutes(subnets: List, routes: List): List { - println() - println("Verifying routes...") - - if (subnets.isEmpty()) return routes - - val removals: MutableList = mutableListOf() - for (subnet in subnets) { - for (route in routes) { - if (subnet.info.overlaps(route.info)) { - println("Subnet ${subnet.info.cidrSignature} found route ${route.info.cidrSignature}...removing route") - removals.add(route) - } - } - } - - return routes.toMutableList().apply { removeAll(removals) } - } } fun main(args: Array) { @@ -227,19 +206,3 @@ private fun createBitFlipMask(prefix: Int): Int { return base } -private fun formatRoute(octets: IntArray, index: Int, result: Int, prefixLength: Int): Subnet { - val subnet = StringBuilder() - - octets.forEachIndexed{ i, value -> - when { - i == index -> subnet.append(result.toString()) - i < index -> subnet.append(value) - else -> subnet.append(0) - } - if (i != octets.size - 1) { - subnet.append(".") - } - } - return Subnet("$subnet/$prefixLength") -} - diff --git a/src/main/kotlin/extensions.kt b/src/main/kotlin/extensions.kt deleted file mode 100644 index 3b67762..0000000 --- a/src/main/kotlin/extensions.kt +++ /dev/null @@ -1,26 +0,0 @@ -internal fun Address.normalizeAddress(): String { - return String.format( - "%s.%s.%s.%s", - value.split(".")[0].padStart(3, '0'), - value.split(".")[1].padStart(3, '0'), - value.split(".")[2].padStart(3, '0'), - value.split(".")[3].padStart(3, '0') - ) -} - -internal fun MutableMap.toSubnet(): List { - return this.asSequence() - .map { Subnet("${it.key.value}/${it.value}").apply { isInclusiveHostCount = true } } - .toList() - .sortedBy { it.info.getAddress().normalizeAddress() } -} - -internal fun List.addZeroZeroZeroZero(): List { - return this.toMutableList().apply { - add(Subnet("0.0.0.0/32")) - }.toList() -} - - - -