One limitation of Mikrotik‘s DNS and DHCP implementations is that you cannot easily sync hostnames from DHCP requests into DNS. This is a standard feature of many routers. On OpenWRT routers (which use dnsmasq), if a PC called “pc1” requests an IP address, any other PC can then “ping pc1”. Fortunately, we can script this behaviour on Mikrotik.
Several Mikrotik users have written scripts to do this. However, all lacked something I needed:
- Operating on only one DHCP server, to only one zone (domain suffix). I have routers with multiple interfaces and DHCP servers, each of which should be synchronised to only one zone.
- Avoid operating on manually-created static DNS entries. All scripts I found can potentially delete static records you create.
- Name their variables using correct jargon for the protocols involved, such as DNS “records” and DHCP “leases”. This isn’t necessary, but it sure makes the script easier to understand.
The Script
Here is my script to sync DHCP and DNS on Mikrotik routers.
# Creates static DNS entres for DHCP clients in the named DHCP server. # Hostnames passed to DHCP are appended with the zone. # Set the first two variables according to your installation. :local dhcpserver "dhcp1" :local zone "home.example.com" # Set the TTL to the scheduler frequency for this script. :local ttl "00:05:00" # Clear old static DNS entries matching the zone and TTL. /ip dns static :foreach dnsrecord in=[find where name ~ (".*\\.".$zone) ] do={ :local fqdn [ get $dnsrecord name ] :local hostname [ :pick $fqdn 0 ( [ :len $fqdn ] - ( [ :len $zone ] + 1 ) ) ] :local recordttl [get $dnsrecord ttl] :if ( $recordttl != $ttl ) do={ :log debug ("Ignoring DNS record $fqdn with TTL $recordttl") } else={ /ip dhcp-server lease :local dhcplease [ find where host-name=$hostname and server="$dhcpserver"] :if ( [ :len $dhcplease ] > 0) do={ :log debug ("DHCP lease exists for $hostname in $dhcpserver, keeping DNS record $fqdn") } else={ :log info ("DHCP lease expired for $hostname, deleting DNS record $fqdn") /ip dns static remove $dnsrecord } } } # Create or update static DNS entries from DHCP server leases. /ip dhcp-server lease :foreach dhcplease in=[find where server ~ ("$dhcpserver")] do={ :local hostname [ get $dhcplease host-name ] :if ( [ :len $hostname ] > 0) do={ :local dhcpip [ get $dhcplease address ] :local fqdn ( $hostname . "." . $zone ) /ip dns static :local dnsrecord [ find where name=$fqdn ] :if ( [ :len $dnsrecord ] > 0 ) do={ :local dnsip [ get $dnsrecord address ] :if ( $dnsip = $dhcpip ) do={ :log debug ("DNS record for $fqdn to $dhcpip is up to date") } else={ :log info ("Updating DNS record for $fqdn to $dhcpip") /ip dns static remove $dnsrecord /ip dns static add name=$fqdn address=$dhcpip ttl=$ttl } } else={ :log info ("Creating DNS record for $fqdn to $dhcpip") /ip dns static add name=$fqdn address=$dhcpip ttl=$ttl } } }
Installation
To use this script, install it as a new script in System -> Scripts. Edit the first two variables with the name of your DHCP server and the matching DNS zone you want to create, and save the script. Finally, create a job in System -> Scheduler to run the script every five minutes.
How does it work?
The script has two main loops. The first loop operates on DNS records, clearing any expired records it previously created. For each DNS record with TTL of 5 minutes, it checks to see if a corresponding DHCP lease exists. If not, it deletes the record.
The second loop does the real work. It operates on DHCP leases from the named DHCP server. For each lease, it checks that lease has a corresponding DNS record matching the zone. If yes, it checks that the DHCP lease IP matches the DNS record and updates it. If it didn’t have a matching DNS record, it creates a new one.
All DNS records made by this script have the DNS zone appended, and have a TTL of 5 minutes. To ensure a static DNS entry is ignored by this script, just ensure it has any other TTL, or doesn’t match the zone. Otherwise it will try to manage it.
This script is reasonably fast on my test routers. I’ve attempted to add additional logic to sanitise hostnames from DHCP (which can have invalid characters in Mikrotik’s DHCP server), but the loops required caused the script to run much slower. This is a limitation of Mikrotik script language, which practically requires you write inefficient loops and string construction functions for anything complicated.
Debugging
To watch the script in action, turn on logging:
/system logging add topics=script
Updates
The latest version will always be available on Github.
-
Thanks for taking the time to write this, it’s just what I was looking for :-) As a side-note, where/what are you using for this blog-site (I really like the layout).
-
Hi, thanks for the script.
I have a query. I have 3x interfaces with a different dhcp range on each interface. This means there are 3x “dhcp servers”. How do I get the script to manage each dhcp server’s ip’s?
I created 3x scripts (one for each server), but each script removes entries from the other scripts, resulting in only entries for one dhcp server.
Hopefully I am doing something wrong.Thanks in advance.
-
I have this installed and running, but it doesn’t seem to be doing anything. I also turned on debugging but nothing interesting appears in teh logs.
The schedule run count is incrementing, so I know it’s running.
I’m on 6.25 if that makes a difference.
Thanks in advance for any info!
-
-
18 comments
Comments feed for this article
Trackback link: https://www.tolaris.com/2014/09/27/synchronising-dhcp-and-dns-on-mikrotik-routers/trackback/