Updating HE.NET Dynamic DNS

I know this is an old thread, but since it’s the first result when googling for “mikrotik he.net ddns”, I thought it might be useful to share my version of the script which contains a few improvements:

  • Support for IPv6 (you’ll need to specify an interface and an IPv6 pool)
  • Error handling (i.e. when the IPv6 update fails, the IPv4 will still be published)
  • Does not write temporary files
  • Does not rely on an external service to determine the IP addresss

Here’s the script:

# RouterOS dynamic DNS Update Script for he.net
# =============================================

# make global to preserve IP and IPv6 address across script executions
:global ipddns
:global ipv6ddns

:local ddnshost "myhost.example.com"
:local ddnspass "MY_SECURE_PASSWORD"
:local waninterfacev4 "V4_INTERFACE_NAME" # Interface to retrieve WAN IPv4 address from (i.e. pppoe-out1)
:local waninterfacev6 "V6_INTERFACE_NAME" # Interface to retrieve WAN IPv6 address from (i.e. bridge1)
:local ipv6pool "YOUR_IP6_POOL" # Pool that IPv6 address needs to be part of
:local url "https://dyn.dns.he.net/nic/update"

:local ipfresh ""
:local ipv6fresh ""
:local ipv4error false
:local ipv6error false

:do {
    # get current IP address from WAN interface
    :set ipfresh [/ip address get [find where interface=$waninterfacev4] value-name=address]
    # Check if ipfresh is empty
    :if ($ipfresh = "") do={
        :log error ("DDNS: Failed to get IP address from interface $waninterfacev4.")
        :set ipv4error true
    }
    :if ([ :typeof $ipfresh ] = nil ) do={
        :log error ("DDNS: No IP address on $waninterfacev4")
        :set ipv4error true
    }
} on-error={
    :log error "DDNS: An error occurred while getting the current IP address from WAN interface."
    :set ipv4error true
}

:do {
    # get current IPv6 address from LAN interface
    :set ipv6fresh [/ipv6/address get [find where from-pool="telekom-ipv6" interface=$waninterfacev6] address]
    # Check if ipv6fresh is empty
    :if ($ipv6fresh = "") do={
        :log error ("DDNS: Failed to get IPv6 address from interface $waninterfacev6.")
        :set ipv6error true
    }
    :if ([ :typeof $ipv6fresh ] = nil ) do={
        :log error ("DDNS: No IPv6 address on $waninterfacev6")
        :set ipv6error true
    }
} on-error={
    :log error "DDNS: An error occurred while getting the current IPv6 address from LAN interface."
    :set ipv6error true
}

# extract the portion of the IP address before the last / character
:do {
    # extract the portion of the IP address before the last / character
    :for i from=( [:len $ipfresh] - 1) to=0 do={ 
        :if ( [:pick $ipfresh $i] = "/") do={ 
            :set ipfresh [:pick $ipfresh 0 $i];
        } 
    }

    :for i from=( [:len $ipv6fresh] - 1) to=0 do={ 
        :if ( [:pick $ipv6fresh $i] = "/") do={ 
            :set ipv6fresh [:pick $ipv6fresh 0 $i];
        } 
    }
} on-error={
    :log error "DDNS: An error occurred while extracting the portion of the IP addresses."
}

:do {
    # Check and update IPv4 address
    :if ($ipv4error) do={
        :log warning "DDNS: Skipping IPv4 update due to previous error."
    } else {
        :if ($ipddns = $ipfresh) do={
            :log info ("DDNS: IP address is already up to date")
        } else {
            :log info ("DDNS: IP address has changed from $ipddns to $ipfresh, sending update...")
            :local body "hostname=$ddnshost&password=$ddnspass&myip=$ipfresh"
            :local result [/tool fetch mode=https output=user url=$url http-method=post http-data=$body as-value]

            :if ($result->"status" != "finished") do={
                :log error ("DDNS: Failed to send IPv4 update")
            } else {
                :log info ("DDNS: Response from server: " . $result->"data")
                :if (([:find ($result->"data") "good"] = 0) or ([:find ($result->"data") "nochg"] = 0)) do={
                    :set ipddns $ipfresh
                }
            }
        }
    }
} on-error={
    :log error "DDNS: An error occurred while checking and updating the IPv4 address."
}

:do {
    # Check and update IPv6 address
    :if ($ipv6error) do={
        :log warning "DDNS: Skipping IPv6 update due to previous error."
    } else {
        :if ($ipv6ddns = $ipv6fresh) do={
            :log info ("DDNS: IPv6 address is already up to date")
        } else {
            :log info ("DDNS: IPv6 address has changed from $ipv6ddns to $ipv6fresh, sending update...")
            :local body "hostname=$ddnshost&password=$ddnspass&myip=$ipv6fresh"
            :local result [/tool fetch mode=https output=user url=$url http-method=post http-data=$body as-value]

            :if ($result->"status" != "finished") do={
                :log error ("DDNS: Failed to send IPv6 update")
            } else {
                :log info ("DDNS: Response from server: " . $result->"data")
                :if (([:find ($result->"data") "good"] = 0) or ([:find ($result->"data") "nochg"] = 0)) do={
                    :set ipv6ddns $ipv6fresh
                }
            }
        }
    }
} on-error={
    :log error "DDNS: An error occurred while checking and updating the IPv6 address."
}

It’s also on GitHub as a Gist.

I’ve tested this on RouterOS 7.9.