Tuesday 31 March 2015

Mikrotik Synchronize Address List

15:11 Posted by Jurgens Krause , , , 2 comments
This is a simple script solution to synchronize small address lists between Mikrotik routers. It is limited by the fact that there is a 4096 byte limit for variables in Mikrotik Scripts. I have maximized the number if entries you can sync by putting only the list name and address in the file


On the host router add the following script:

#Semicolon separated list of Address Lists you want to sync
:local lists {"List1";"List2"}

#Filename of export (must match receiving router import filename
:local exportFile ExportAddressList

/file remove [:file find name=$exportFile]

:local ipAddress
:local listEntry
:local fileContents

:set fileContents ""

:foreach listName in=$lists do={
  :foreach listEntry in=[/ip firewall address-list find where list=$listName] do={
    :set ipAddress [/ip firewall address-list get $listEntry address]
    :log info "=$listName"
    :set fileContents "$fileContents$listName=$ipAddress\n"
    }
}
/file print file=$exportFile
/file set $exportFile contents=$fileContents


On the client router, add the following script.
#Filename of export (must match receiving router import filename
:local importFile ExportAddressList.txt

#IP Address of router with existing address list(s)
:local hostIP 1.2.3.4

#FTP Username and Password
:local ftpUser username
:local ftpPassword userpassword

/tool fetch address=$hostIP src-path=$importFile dst-path=$importFile mode=ftp user=$ftpUser password=$ftpPassword
:local fileContent [/file get [/file find name=$importFile] contents]

#IMPORT NEW ROUTES
{
#Declare Local Variables
    :local contentLength [:len $fileContent];
    :local lineEnd 0
    :local lineContent ""
    :local lastEnd 0
    :local addressList
    :local addressListEnd
    :local ipAddress
    :local ipAddressStart
    :local lineLength

    :while ($lineEnd < $contentLength) do={
        :set lineEnd [:find $fileContent "\n" $lastEnd]
        :if ([:len $lineEnd] = 0) do={
            :set lineEnd $contentLength
        }
        :set lineContent [:pick $fileContent $lastEnd $lineEnd]
        :set lastEnd ($lineEnd +1)
        : if ($lineContent !="") do={
            :set addressListEnd [:find $lineContent "=" -1]
            :set addressList [:pick $lineContent 0 $addressListEnd]
            :set lineLength [:len $lineContent]
            :set ipAddressStart ($addressListEnd+1)
            :set ipAddress [:pick $lineContent $ipAddressStart $lineLength]
            /ip firewall address-list
            :if ([/ip firewall address-list find address=$ipAddress]="") do={add list=$addressList address=$ipAddress comment="Imported from $hostIP"}
        }
#^ENDIF^
    }
#^ENDWHILE^

}
#END OF IMPORT


#REMOVE ROUTES THAT ARE NO LONGER IN HOST LIST
{
    :local listEntry
    :local listName
    :local listIP
    :local listComment
    :local findResult
 
    :foreach listEntry in=[/ip firewall address-list find] do={
        :set listIP [/ip firewall address-list get $listEntry address]
        :set listName [/ip firewall address-list get $listEntry list]
        :set listComment [/ip firewall address-list get $listEntry comment]
        :if ($listComment ="Imported from $hostIP") do={
            :set findResult [:find $fileContent "$listName=$listIP" -1]
            :set findResult "a$findResult"
            :if ($findResult ="a") do={
                /ip firewall address-list remove $listEntry
            }
        }
    }
}
#END OF REMOVE ROUTES


Remember to replace the variables with your values.
You can now set the scheduler to synchronize the lists at regular intervals.

2 comments:

  1. Thanks, pretty neat script!

    ReplyDelete
  2. Thank you very much for sharing you work. One question, how could we overcome the 4096 byte limit? My address list are longer than that. Any idea would be very mucho appreciated.

    ReplyDelete