The function of obtaining an array of subnets between two ip4 addresses. May be useful for organizing addresses database. The order of the transmitted addresses does not matter. And function for obtaining an array of subnets included in a given one (warning, do not call this function with a large prefix increment value, now the limit is no more than 12, but this is not small).
# return common subnet prefix of two ip
:global getIpCommonPrefix do={
:local xored ([:tonum [:toip $1]] ^ [:tonum [:toip $2]])
:local prefix 32
:while ($xored) do={
:set xored ($xored >> 1)
:set prefix ($prefix - 1)
}
:return $prefix
}
:put [$getIpCommonPrefix 192.168.88.77 192.168.88.111]
# return array with subnets between two ip
:global getSubnetsBetweenIp do={
:local ip1 [:toip $1]
:local ip2 [:toip $2]
:local subnets [:toarray ""]
:global getIpCommonPrefix
:local prefix [$getIpCommonPrefix $1 $2]
:local submask (255.255.255.255 << (32 - $prefix))
:local addrspace (~$submask)
:local network
:local broadcast
:if ($prefix = 0) do={
:set network 0.0.0.0
:set broadcast 255.255.255.255
} else={
:set network ($ip1 & $submask)
:set broadcast ($ip1 | $addrspace)
}
:if ($ip1 > $ip2) do={
:set ip1 $ip2
:set ip2 [:toip $1]
}
:if ($ip1 = $network && $ip2 = $broadcast) do={
:set subnets ($subnets, ($network."/".$prefix))
} else={
:local finded1
:local finded2
:while ($prefix < 32 && !($finded1 = $ip1 && $finded2 = $ip2)) do={
:set prefix ($prefix + 1)
:set submask (255.255.255.255 << (32 - $prefix))
:set addrspace (~$submask)
:set network ($ip1 & $submask)
:local loop true
:while ($loop && $network <= $ip2) do={
:if ($network >= $finded1 && $network <= $finded2) do={
:if ($finded2 < 255.255.255.255) do={
:set network ($finded2 + 1)
} else={
:set loop false
}
}
:if ($loop) do={
:set broadcast ($network | $addrspace)
:if ($network >= $ip1 && $broadcast <= $ip2) do={
:if ($network < $finded1 || [:typeof $finded1] = "nothing") do={ :set finded1 $network }
:if ($broadcast > $finded2 || [:typeof $finded2] = "nothing") do={ :set finded2 $broadcast }
:set subnets ($subnets, ($network."/".$prefix))
}
:if ($broadcast < 255.255.255.255) do={
:set network ($broadcast + 1)
} else={
:set loop false
}
}
}
}
}
:return $subnets
}
:put [$getSubnetsBetweenIp 0.0.0.1 255.255.255.254]
:put [$getSubnetsBetweenIp 255.255.255.254 0.0.0.0]
:put [$getSubnetsBetweenIp 0.0.0.1 255.255.255.255]
:put [$getSubnetsBetweenIp 0.0.0.0 255.255.255.255]
# return an array of subnets included in the given subnet
:global getIncludedSubnets do={
:local prefix [:tonum [:pick $1 ([:find $1 "/"] + 1) [:len $1]]]
:local add [:tonum $2]
:local subnets [:toarray ""]
:if ($add >= 0 && $add <= 12 && ($prefix + $add) <= 32) do={
:local ip [:toip [:pick $1 0 [:find $1 "/"]]]
:local submask (255.255.255.255 << (32 - $prefix))
:local addrspace (~$submask)
:local network
:local broadcast
:if ($prefix = 0) do={
:set network 0.0.0.0
:set broadcast 255.255.255.255
} else={
:set network ($ip & $submask)
:set broadcast ($ip | $addrspace)
}
:if ($add = 0) do={
:set subnets ($subnets, ($network."/".$prefix))
} else={
:set prefix ($prefix + $add)
:local step (1 << (32 - $prefix))
:local loop true
:while ($loop && $network <= $broadcast) do={
:set subnets ($subnets, ($network."/".$prefix))
:if ((255.255.255.255 - $network) >= $step) do={
:set network ($network + $step)
} else={
:set loop false
}
}
}
}
:return $subnets
}
:put [$getIncludedSubnets 192.168.88.77/24 5]
:put [$getIncludedSubnets 192.168.88.77/24 0]
:put [$getIncludedSubnets 255.255.255.255/0 0]
Example of addresses searching in lists using associative arrays - may give better performance in scripts for works with large addresses base. Building array is called once, then, when adding new address to list from which the array is compiled, add the address to list and to array. Weak point of algorithm is the presence of large subnets in lists. Search for each address takes place in a cycle with a number of steps: (33 minus the minimum prefix value in the checked lists), the presence of subnets such as 10.0.0.0/8 in array significantly affects search performance. It is better to check whether an address is included in large subnets separately.
:global mylist [:toarray ""]
:global minprefix 32
:local addrlist [/ip firewall address-list print detail as-value where ";allowed-ip;white-list;local-networks;service-addresses;test-list;"~list]
:foreach addr in=$addrlist do={
:local ip
:local prefix
:if ([:find ($addr->"address") "/"] > 0) do={
:set ip [:toip [:pick ($addr->"address") 0 [:find ($addr->"address") "/"]]]
:set prefix [:tonum [:pick ($addr->"address") ([:find ($addr->"address") "/"] + 1) [:len ($addr->"address")]]]
} else={
:set ip [:toip ($addr->"address")]
:set prefix 32
}
:if ([:typeof $ip] != "nil") do={
:local submask (255.255.255.255 << (32 - $prefix))
:local subnet
:if ($prefix = 0) do={
:set subnet "0.0.0.0"
} else={
:set subnet [:tostr ($ip & $submask)]
}
:if ([:typeof ($mylist->$subnet)] = "nothing" || ($mylist->$subnet) > $prefix) do={
:set ($mylist->$subnet) $prefix
}
:if ($minprefix > $prefix) do={
:set minprefix $prefix
}
}
}
:global inMyList do={
:global mylist
:global minprefix
:local network [:toip $1]
:local submask 255.255.255.255
:for prefix from=32 to=$minprefix step=-1 do={
:local subnet [:tostr $network]
:if ([:typeof ($mylist->$subnet)] != "nothing" && ($mylist->$subnet) <= $prefix) do={ :return true }
:set submask ($submask << 1)
:set network ($network & $submask)
}
:return false
}
# Usage test
:local ip 192.168.88.1
:for i from=1 to=254 step=1 do={
:if ([$inMyList $ip]) do={
:put $ip
} else={
:set ($mylist->[:tostr $ip]) 32
# /ip firewall address-list add address=$ip list=test-list
}
:set ip ($ip + 1)
}
There is also a tool for working with address databases, if anyone is interested syo.su