I wrote a function $FAKEAL to generate random IPs in an address-list, e.g. ones that might require aggregation. Originally created to test an @rextended prefix aggregator script (http://forum.mikrotik.com/t/find-addresses-with-same-octets/168376/27), but likely has additional uses (e.g. testing network performance with large address lists present). The code is a section below.
What it does?
Creates random IPs addresses (e.g. /32 prefix) in an specific /ip/firewall/address-list, with controls on the possible range of address (spread=) and how “full” the range is (density=)
To see it in action… the basic uses “spread=” to control the range of IPs added to list (by /24 subnet’s…,so spread=1 mean a /24) & density=1 means the range is populated with 1% of possible IP addresses in the resulting address-list.
[admin@router] /> $FAKEAL spread=1 density=1
using address-list FAKEAL
remove FAKEAL done
adding 98% 169.254.0.250 (added 4 at 250 of 254)
runtime 00:00:00
done! wanted 2 got 4 (off by -2) in FAKEAL (length 4)
[admin@router] /> /ip/firewall/address-list print
Columns: LIST, ADDRESS, CREATION-TIME
# LIST ADDRESS CREATION-TIME
0 FAKEAL 169.254.0.65 2023-08-01 13:28:46
1 FAKEAL 169.254.0.81 2023-08-01 13:28:46
2 FAKEAL 169.254.0.102 2023-08-01 13:28:46
3 FAKEAL 169.254.0.250 2023-08-01 13:28:46
The function $FAKEAL provides a help to understand the options:
$FAKEAL help
Usage:
$FAKEAL [spread=0..119304647] [density=1..100] [list=list_name] [replace={no|false|off|0}] [start=ip.ad.dr.ess]
spread= num of /24's to distribute random entires over (e.g. how many / 254), default is 1 (/24)
density= percentage (as int) of used address over the total range (i.e. 50 = 50% of possible IP), default is 25
list= default is FAKEAL but can be any valid name for /ip firewall address-list
start= the first possible IP address to use, default is 169.254.0.0
replace= any previous list created by $FAKEAL is removed, use 'replace=no' to keep an old entires in list, default is yes
The needed function code to do this is here:
:global FAKEAL do={
:local defaultlname [:pick $0 1 255]
:local defaultspread 1
:local defaultdensity 25
:local defaultstart 169.254.0.0
:local todefault do={:if ([:typeof $1]="$2") do={:return $1 } else={:return $3 }}
:local invalidchr do={:if ($1~"[\24\3F\\\60\01-\19\7F-\FF]") do={:return true} else={:return false}}
:local listname [$todefault $list "str" $defaultlname ]
:local lspread [$todefault [:tonum $spread ] "num" $defaultspread ]
:local ldensity [$todefault [:tonum $density] "num" $defaultdensity ]
:local lstart [$todefault [:toip $start ] "ip" $defaultstart ]
:local lreplace [$todefault [:tostr $replace ] "str" true ]
:if ([$invalidchr $listname] ) do={:error "list= contains invalid chars" }
:if (($lspread < 0) or ($lspread > 119304647)) do={:error "spread= is out of range" }
:if (($ldensity < 1) or ($ldensity > 100 )) do={:error "density= is out of range" }
:if ($replace~"^(no|false|off)\$") do={:set lreplace false} else={:set lreplace true}
:if ($1="help") do={
:put "Usage:"
:put "$0 [spread=0..119304647] [density=1..100] [list=list_name] [replace={no|false|off|0}] [start=ip.ad.dr.ess]"
:put " spread= num of /24's to distribute random entires over (e.g. how many / 254), default is $defaultspread (/$($defaultspread*24))"
:put " density= percentage (as int) of used address over the total range (i.e. 50 = 50% of possible IP), default is $defaultdensity"
:put " list= default is $defaultlname but can be any valid name for /ip firewall address-list"
:put " start= the first possible IP address to use, default is $defaultstart"
:put " replace= any previous list created by $0 is removed, use 'replace=no' to keep an old entires in list, default is yes"
:return [:nothing]
}
:local possible (254*$lspread)
:local howmany ($possible*$ldensity/100)
:local tstart
:local NOW do={:do { :return [:timestamp] } on-error={ :return [/system clock get time]}}
/ip firewall address-list {
:put "using\taddress-list $listname"
:if ($lreplace) do={
:put "remove\t$listname pending"
remove [find list=$listname]
/terminal cuu
:put "remove\t$listname done "
}
:set tstart [$NOW]
:put ""
:local numadded 0
:local skipped [:toarray ""]
:local rndip
:for listitem from=0 to=($possible-1) do={
# so we loop through possible range...
:do {
# apply the "odds" the IP should appear...
:local varinum [:rndnum from=0 to=(10000 / $ldensity)]
# if :rndnum is 0... which it would be 1 out of $ldensity times
:if ($varinum < 100) do={
# add an possible IP to addresss, otherwise move on without adding
:set rndip ($lstart + $listitem)
add address=$rndip list=$listname
:set numadded ($numadded+1)
/terminal cuu
:put "adding\t$((($listitem+1)*100)/($possible))%\t$rndip\t(added $numadded at $listitem of $possible) "
} else={
# DEBUG
#:put "rejected $rndip with $varinum"
}
} on-error={:set skipped ($skipped,$rndip)}
}
:if ([:len $skipped] > 0) do={
:put "skipped\t$[:len $skipped] IPs, likely because the IP was already in list"
}
:put "runtime\t$([:pick ([$NOW]-$tstart) 0 8])"
:put "done!\twanted $howmany got $numadded off by $($howmany-$numadded) ($(($howmany-$numadded) * 100 / $howmany)%) in $listname (length $[:len [find list=$listname]])"
}
}
# show help
$FAKEAL help
# create ~25 IPs (density=10 is 10%) in an address-list over 169.254.0.0/24 (spread=1 is 254 IPs)
#$FAKEAL spread=1 density=10
# create ~645 IPs (density=1 is 1%) in specific address-list ("rndvente") over 10.20.0.0 (so spread=254 is /16)
#$FAKEAL spread=254 density=1 list=rndtens start=10.20.0.0
Performance isn’t bad using the approach to loop through all possible IP THEN decide randomly if that particular possible address should be added. Another approach was used in function called $fantacylist in http://forum.mikrotik.com/t/find-addresses-with-same-octets/168376/1 but this version is WAY faster than “hope and prey” method in $fantacylist.
In quick test, creating a list that ended up being 126471 address-list entries, took about 2 minutes (well 00:02:09) on a RB1100AHx4. But this method is by far quicker than checking if any item is the address-list before adding it – even at the expensive of looping >500,000 times to do it in the example below.
$FAKEAL spread=2000 density=30 list=rndvente start=10.20.0.0
using address-list rndvente
remove rndvente done
adding 99% 10.27.192.84 (added 126471 at 507988 of 508000)
runtime 00:02:09
done! wanted 152400 got 126471 (off by 25929) in rndvente (length 126471)