Emerging Member
Posts: 58
Registered: ‎04-18-2014
Kudos: 41
Solutions: 1

DNS resolution of local hosts

[ Edited ]

In some cases, the hostfile-update feature seems to fail to update leases correctly. I also wanted to have the static-mappings included in the hosts file for DNS resolution. Therefore I have made the following script that seems to work for my configuration.

 

First disable hostfile-update option

 

set service dhcp-server hostfile-update disable
commit

 

Create a file with the script below and make it executable. Place it in /config/scripts/ to make it persistent in case of firmware update. Edit the LAN_INTERFACE script variable to match your configuration.

 

#!/bin/sh

#
# Stores the hostnames from the DHCP lease file and fixed addresses (static) into the hosts file.
# This script is triggered by inotify on every modification of either the lease file or the
# dhcpd.conf file where the static mappings are defined
#

# Enter here your interface (dev) facing the LAN
LAN_INTERFACE="eth0"

# Script variables
LEASE_FILE=/var/run/dhcpd.leases
HOSTS_FILE=/etc/hosts
TMP_OLD=/tmp/myhosts.o
TMP_NEW=/tmp/myhosts.n
TMP_FIXED=/tmp/myhosts.f
DHCP_CONF=/opt/vyatta/etc/dhcpd.conf

# removes the useless <hostname> 127.0.1.1 from /etc/hosts
sed -i '/127.0.1.1/d' $HOSTS_FILE

# For this router, determine the IP, hostname and mac of the interface facing LAN
THIS_IP=$(ip route list dev $LAN_INTERFACE | awk '{print $NF}')
THIS_DOMAIN=$(awk '/^domain/{print "."$2}' /etc/resolv.conf)
THIS_HOSTNAME=$(hostname)
THIS_MAC=$(ifconfig $LAN_INTERFACE | awk '/HWaddr/{print $NF}')

# store current hosts mapping in temporary file
awk '/myhosts START/,/myhosts END/' $HOSTS_FILE > $TMP_OLD

# print start flag in new temp file
printf "# myhosts START\n" > $TMP_NEW

# extract and print fixed-address from /opt/vyatta/etc/dhcpd.conf file
printf "# Fixed addresses\n" >> $TMP_NEW
printf "%s\t%-35s\t%-30s# %s This router\n" "$THIS_IP" "$THIS_HOSTNAME$THIS_DOMAIN" "$THIS_HOSTNAME" "$THIS_MAC" > $TMP_FIXED
awk -v domain="$THIS_DOMAIN" '
BEGIN{FS="\t|;| "}
/shared-network/{ntwk=$2}
/host/{h=$4; f=1}
/fixed-address/{ip=$(NF-1)}
f && /hardware/{printf "%s\t%-35s\t%-30s# %s %s\n", ip, h domain, h, $(NF-1), ntwk; f=0}
' $DHCP_CONF >> $TMP_FIXED
sort -u $TMP_FIXED >> $TMP_NEW

# extract and print leased addresses from /var/run/dhcpd.leases
printf "# Leased addresses\n" >> $TMP_NEW
awk -v domain="$THIS_DOMAIN" '
BEGIN{FS=" +|;"}
/^lease/{ip=$2; f=1}
f && /hardware/{m=$4}
f && /shared-network/{ntwk=$2}
f && /hostname/{gsub(/\"/, "", $3); printf ("%s\t%-35s\t%-30s# %s %s\n", ip, $3 domain, $3, m, ntwk); f=0}
' $LEASE_FILE | sort -k1,1 -u >> $TMP_NEW

# print end flag
printf "# myhosts END\n" >> $TMP_NEW

# if any change between existing myhosts and new ones, store them in main hosts file and reload dnsmasq
cmp -s $TMP_OLD $TMP_NEW
if [ $? -ne 0 ];then
# delete existing dnsmasq hosts btw start and end flags from hosts file
sed -i '/myhosts START/,/myhosts END/d' $HOSTS_FILE

# add new ones
cat $TMP_NEW >> $HOSTS_FILE

# clear dnsmasq cache, reloads the hosts file and log the update
logger -s [${0##*/} - inotify] '/etc/hosts file updated'
kill -HUP $(pidof dnsmasq)
fi

# print existing hosts file and cleanup temp files
cat $HOSTS_FILE
rm /tmp/myhosts.*

To see that script in action, just execute it. Now, that script needs to be triggered on every change in the lease and dhcpd.conf file. For that, install inotify-tools from the repo. If you don't know how to install a Debian package, read this.

 

Once installed, add this script in /config/scripts/post-config.d/ so that is will be executed on reboot.

  

#!/bin/sh

# Execute update-dhcp-leases every time there is a change in the dhcpd lease and conf file

# exit if inotify-tools not installed
type inotifywait 2> /dev/null 1>&2 || { \
	printf "\ninotifywait not found.\ninstall inotify-tools and run this script again.\nExiting\n\n"
	exit 1
}

# listening to any changes in leases and/or static-mapping
while inotifywait -e modify /var/run/dhcpd.leases /opt/vyatta/etc/dhcpd.conf;do 
	/config/scripts/update-hosts-file
done

 

Execute that script and test it by adding a new static-mapping in GUI or CLI and watch it executing the update-hosts-file script.

In my next life I will try to be a native speaker of English, in the meantime please forgive my typos.
Previous Employee
Posts: 13,551
Registered: ‎06-10-2011
Kudos: 5471
Solutions: 1656
Contributions: 2

Re: DNS resolution of local hosts

Great work! We should look into whether this may be a better implementation that could replace the current one. Thanks for your contribution!

Regular Member
Posts: 501
Registered: ‎12-17-2015
Kudos: 200
Solutions: 10

Re: DNS resolution of local hosts

This is great! Only issue I've run into is manually running the update-hosts-file results in: 

 

line 69: kill: (1136) - Operation not permitted. 

 

I'm assuming once inotify is involved, that stops (since it'll be running as su). 

 

Also, I assume the inotify script will continue to run after a reboot? 

USG, UniFi AP AC HD, UniFi US-16-150W, Unifi US-8, UCK-G2-Plus, 4x UVC-G3, UVC-G3-Flex
Emerging Member
Posts: 58
Registered: ‎04-18-2014
Kudos: 41
Solutions: 1

Re: DNS resolution of local hosts

That script need to be run as root.

 

As for the reboot, yes, the inotify watchdog script is placed in config/scripts/post-config.d/  and will be automatically executed after reboot.

In my next life I will try to be a native speaker of English, in the meantime please forgive my typos.
Regular Member
Posts: 501
Registered: ‎12-17-2015
Kudos: 200
Solutions: 10

Re: DNS resolution of local hosts

Excellent. Since you didn't have names for the scripts, I did name them: 

 

/config/scripts/update-hosts-file

/config/scripts/post-config.d/inotify-update-hosts-file

 

The first one I gleened the name from the second, but had no idea if there was any naming convention for the second. =P

 

Thanks for this! I hope it gets implemented in core, because the way core does it is just weird (doesn't update hosts unless there's an actual dhcp request, which means if you statically assign an IP on the client, you don't get the entry. It also seemed to leave off the domain... just, very weird. 

USG, UniFi AP AC HD, UniFi US-16-150W, Unifi US-8, UCK-G2-Plus, 4x UVC-G3, UVC-G3-Flex
Regular Member
Posts: 501
Registered: ‎12-17-2015
Kudos: 200
Solutions: 10

Re: DNS resolution of local hosts

[ Edited ]

Oh, so one question about this... 

 

What would be an easy way to have this script add just the hostname and IP to hosts? because right now, for some reason, EdgeOS doesn't actually try adding "domain" to the hostname, so it just says it can't resolve. 

 

For example:

 

admin@enty:/config/scripts$ ping sauron

ping: unknown host sauron

admin@enty:/config/scripts$ ping sauron.middleearth.dom

PING sauron.middleearth.dom (10.0.2.10) 56(84) bytes of data.

64 bytes from sauron.middleearth.dom (10.0.2.10): icmp_req=1 ttl=64 time=0.512 ms

 

Any thoughts? This, really, is mainly causing an issue since the router can't resolve it's own hostname. =/

USG, UniFi AP AC HD, UniFi US-16-150W, Unifi US-8, UCK-G2-Plus, 4x UVC-G3, UVC-G3-Flex
Emerging Member
Posts: 58
Registered: ‎04-18-2014
Kudos: 41
Solutions: 1

Re: DNS resolution of local hosts

It actually should add the domain name as defined in the system config:

set system domain-name middleearth.com
commit

And run the script again.

 

In my next life I will try to be a native speaker of English, in the meantime please forgive my typos.
Regular Member
Posts: 501
Registered: ‎12-17-2015
Kudos: 200
Solutions: 10

Re: DNS resolution of local hosts

Yes, it DOES add the domain... I want it add both with and without domain.

 

10.0.0.1 server.example.dom server #MAC Address

 

That make more sense? Then the router can resolve both with and without the domain name. 

USG, UniFi AP AC HD, UniFi US-16-150W, Unifi US-8, UCK-G2-Plus, 4x UVC-G3, UVC-G3-Flex
Regular Member
Posts: 501
Registered: ‎12-17-2015
Kudos: 200
Solutions: 10

Re: DNS resolution of local hosts

Friend with some awk knowledge got it fixed. Works great now, and the router can resolve it's own hostname. 

 

#!/bin/sh
#
# Stores the hostnames from the DHCP lease file and fixed addresses (static) into the hosts file.
# This script is triggered by inotify on every modification of either the lease file or the 
# dhcpd.conf file where the static mappings are defined
#

# Enter here your interface (dev) facing the LAN
LAN_INTERFACE="switch0"

# Script variables
LEASE_FILE=/var/run/dhcpd.leases
HOSTS_FILE=/etc/hosts
TMP_OLD=/tmp/myhosts.o
TMP_NEW=/tmp/myhosts.n
TMP_FIXED=/tmp/myhosts.f
DHCP_CONF=/opt/vyatta/etc/dhcpd.conf

# removes the useless 127.0.1.1 from /etc/hosts
sed -i '/127.0.1.1/d' $HOSTS_FILE

# For this router, determine the IP, hostname and mac of the interface facing LAN
THIS_IP=$(ip route list dev $LAN_INTERFACE | awk '{print $NF}')
THIS_DOMAIN=$(awk '/^domain/{print "."$2}' /etc/resolv.conf)
THIS_HOSTNAME=$(hostname)
THIS_MAC=$(ifconfig $LAN_INTERFACE | awk '/HWaddr/{print $NF}')

# print start flag in new temp file
printf "# myhosts START\t%s\n" > $TMP_NEW

# extract and print fixed-address from /opt/vyatta/etc/dhcpd.conf file
printf "# Fixed addresses\n" >> $TMP_NEW
printf "%s\t%-35s %-15s # %s This router\n" "$THIS_IP" "${THIS_HOSTNAME}${THIS_DOMAIN}" "$THIS_HOSTNAME" "$THIS_MAC" > $TMP_FIXED
awk -v domain="$THIS_DOMAIN" '
BEGIN{FS="\t|;| "}
/host/{h=$4; f=1}
/fixed-address/{ip=$(NF-1)}
f && /hardware/{printf "%s\t%-35s %-15s # %s\n", ip, h domain, h, $(NF-1); f=0}
' $DHCP_CONF >> $TMP_FIXED
sort -u $TMP_FIXED >> $TMP_NEW

# extract and print leased addresses from /var/run/dhcpd.leases
printf "# Leased addresses\n" >> $TMP_NEW
awk -v domain="$THIS_DOMAIN" '
BEGIN{FS=" +|;"}
/^lease/{ip=$2; f=1}
f && /hardware/{m=$4}
f && /hostname/{gsub(/\"/, "", $3); printf("%s\t%-35s %-15s # %s\n", ip, $3 domain, $3, m); f=0}
' $LEASE_FILE | sort -k1,1 -u >> $TMP_NEW

# print end flag
printf "# myhosts END\n" >> $TMP_NEW

# store current hosts mapping in temporary file
awk '/myhosts START/,/myhosts END/' $HOSTS_FILE > $TMP_OLD

# if any change between existing hosts and new ones, store them in main hosts file and reload dnsmasq
cmp -s $TMP_OLD $TMP_NEW
if [ $? -ne 0 ];then
# delete existing dnsmasq hosts btw start and end flags from hosts file
sed -i '/myhosts START/,/myhosts END/d' $HOSTS_FILE

# add new ones
cat $TMP_NEW >> $HOSTS_FILE
cat $HOSTS_FILE

# clear dnsmasq cache, reloads the hosts file and log the update
logger ${0##*/}: '/etc/hosts file updated'
kill -HUP $(pidof dnsmasq)
else
echo "no change"
fi

# cleanup temp files
rm /tmp/myhosts.*
USG, UniFi AP AC HD, UniFi US-16-150W, Unifi US-8, UCK-G2-Plus, 4x UVC-G3, UVC-G3-Flex
Emerging Member
Posts: 58
Registered: ‎04-18-2014
Kudos: 41
Solutions: 1

Re: DNS resolution of local hosts

[ Edited ]

@staze wrote:

Yes, it DOES add the domain... I want it add both with and without domain.

 

10.0.0.1 server.example.dom server #MAC Address

 

That make more sense? Then the router can resolve both with and without the domain name. 


Ok, got it. And, yes, it make sense. Just changed a couple of lines in my script above to implement your request.

 

Thanks for your input.

In my next life I will try to be a native speaker of English, in the meantime please forgive my typos.
New Member
Posts: 35
Registered: ‎01-05-2016
Kudos: 22

Re: DNS resolution of local hosts

[ Edited ]

I followed all the steps, but the script does not seem to be running. Nothing changes in the hosts file. Running EdgeOS 1.7.

  1. I installed and confirmed that I have inotify-tools
  2. Files are in the right folders, and permissions are 0755 for both. Owner of both files is an admin, but not root (cannot change permission?)
  3. Updated the script to watch the right port (eth1 in my case)
  4. rebooted router

I tried modifying inotify-update-hosts-file by doing this:

#!/bin/sh

# Execute update-dhcp-leases every time there is a change in the dhcpd lease and conf file

printf "\nhosts updated\n"

# exit if inotify-tools not installed
type inotifywait 2> /dev/null 1>&2 || { \
	printf "\ninotifywait not found.\ninstall inotify-tools and run this script again.\nExiting\n\n"
	exit 1
}

# listening to any changes in leases and/or static-mapping
while inotifywait -e modify /var/run/dhcpd.leases /opt/vyatta/etc/dhcpd.conf;do 
	/config/scripts/update-hosts-file
done

... and watching for "hosts updated" in SSH, but that never happened.

 

What am I doing wrong?

1 cloudkey, 2 networks:
1) USG Pro, 1 x Unifi 16-port PoE, 2 x AC Pro, 1 x AP Pro
2) USG, Unifi 8-port PoE, 1 x AC Lite
Emerging Member
Posts: 58
Registered: ‎04-18-2014
Kudos: 41
Solutions: 1

Re: DNS resolution of local hosts

[ Edited ]

Might sound trivial but did you make the scripts executable?

 

chmod +x script.name
In my next life I will try to be a native speaker of English, in the meantime please forgive my typos.
New Member
Posts: 35
Registered: ‎01-05-2016
Kudos: 22

Re: DNS resolution of local hosts

Yes, I did. Permissions are rwxr-xr-x for both.

Thank you for stepping in to help!

1 cloudkey, 2 networks:
1) USG Pro, 1 x Unifi 16-port PoE, 2 x AC Pro, 1 x AP Pro
2) USG, Unifi 8-port PoE, 1 x AC Lite
New Member
Posts: 35
Registered: ‎01-05-2016
Kudos: 22

Re: DNS resolution of local hosts

[ Edited ]

Bah. Calling the script manually once seems to have made it work fine automatically from then onwards. Very strange.

 

Next problem was that inotifywait would work fine on the cli, but would fail in the script. Rebooting the router fixed it. hosts file now updates and refreshes. Yay!

 

I still can't resolve local hosts on the network from a client PC. Pinging from the router itself works fine. Any tips?

 

 Bah. Again. After restarting dnsmasq it started working . . . .

 

 

1 cloudkey, 2 networks:
1) USG Pro, 1 x Unifi 16-port PoE, 2 x AC Pro, 1 x AP Pro
2) USG, Unifi 8-port PoE, 1 x AC Lite
Regular Member
Posts: 501
Registered: ‎12-17-2015
Kudos: 200
Solutions: 10

Re: DNS resolution of local hosts

sounds like the dhcp server on the router isn't handing out itself as the dns server...? check your dhcp service settings to make sure it's the DNS server listed. 

USG, UniFi AP AC HD, UniFi US-16-150W, Unifi US-8, UCK-G2-Plus, 4x UVC-G3, UVC-G3-Flex
New Member
Posts: 35
Registered: ‎01-05-2016
Kudos: 22

Re: DNS resolution of local hosts

I've been setting the router's IP as the DNS server for ages now, so that wasn't the issue.

Somehow restarting dnsmasq fixed it.

 

I've had several DD-WRT routers for many years (dd-wrt also uses dnsmasq, but probably with better settings), then a couple of ASUS routers with stock firmware, and I haven't had to deal with this for probably at least a decade. Local host resolution always just worked - very early versions of dd-wrt 10 years ago needed a box checked, but that was itand it became the default later.

I also set up bunches of Cisco RV series routers for work, and this works fine on them, too.

Honestly, not having local hosts resolving with ERL as my router was a shock, and needing to go into the cli and muck around with scripts to fix something that I've assumed is very basic and should be working out-of-the-box just doesn't feel right.

1 cloudkey, 2 networks:
1) USG Pro, 1 x Unifi 16-port PoE, 2 x AC Pro, 1 x AP Pro
2) USG, Unifi 8-port PoE, 1 x AC Lite
Regular Member
Posts: 590
Registered: ‎09-10-2012
Kudos: 380
Solutions: 35

Re: DNS resolution of local hosts

[ Edited ]

Hi,

 

I quite like this script and would propose some modifications / added functionality. I could even try to implement it myself, but unfortunately my bash / awk is so horrible, i'd be much faster to rewrite it in perl, and I don't want to "steal" your project. Anyway, if you're still interested in continue development, here are the things that I'd like changed:

 

  1. You currently don't check for expired leases. For performance reasons, isc-dhcp only clears out expired leases out of the dhcpd.lease file every now and again. When creating the hosts file, one should check that "now" is earlier than lease { ends time.
    edit: a simpler alternative is to check that binding state active; is present in the lease declaration, as this means that the lease is still active.

  2. I run multiple "shared-networks" with (potentially) multiple subnets in them. These subnets have different domain names, which can be specified in the dhcpd config shared-network [name] { subnet [network] { option domain-name. This domain-name should override the router's domain (found in /etc/resolv.conf) if present. It also (potentially) means that you'd have to match ips found in the leased list to the subnets in the dhcpd.conf

  3. Adding two host names per IP (hostname and hostname.domain.tld) is IMHO bad design, if not wrong. the domain should be added by the client using either his own domain or dns-search suffixes. IMHO, you should only add host.domain.tld. It may break the scenario when people do not distribute the domain-name via dhcp.

    That being said, by adding the hostname, you save people from not distributing a domain-name via DHCP. In pure theory, those people "just" have a bad config and should live with the consequences. In practice, I could agree to adding two entries per IP (hostname and hostname.domain.tld) if and only if option domain-name is missing from the specific dhcp subnet.

  4. fixed address hosts (static mappings) should be checked whether their name already ends with the domain-name of the subnet they're in. This is actually a hack but unfortunately necessary, because EdgeOS gives you the impression it can declare static mappings not only per shared-network but also per subnet, which it cannot - isc-dhcp treats static mappings globally, so a host defined in subnet 1 is also valid in subnet 2. if you have two hosts with the same name in different subnets, you're in trouble. the obvious way out is to name your hosts including the current domain-name.

  5. this is more a technical detail: in order to save writes to the flash storage, ubnt is actually running the dhcpd.leases file in /var/run, which is tmpfs (They've only implemented this in 1.3.0 or 1.4.0, can't remember). By updating /etc/hosts every time dhcpd.leases changes, you effectively nullify their optimization to save the flash storage Man Happy
    therefore, we probably should write the hosts file into /var/run and add a config-line to dnsmasq with --addn-hosts=/var/run/dynamic-dns-hosts or some other name.

  6. [Optional] Currently, you re-create the entire hosts file on every change of dhcpd.leases. I'm not sure if this is the best way to do it as it takes quite a bit of cpu time to run your script (about 0.5 seconds real cpu time in my case with ~50 leases and ~20 static mappings). It would probably be better to just run a tiny script with dhcpd on commit which just adds the host to the end of our lease file. then, once an hour, we could run the full script, and thereby clearing out all expired leases.

 

 Anyway, let me know what you think and if you have time / motivation to implement any of those ideas. If you don't feel like implementing any of this, would you mind if I "took over" your project / idea?

Regular Member
Posts: 501
Registered: ‎12-17-2015
Kudos: 200
Solutions: 10

Re: DNS resolution of local hosts

Great comments. 

 

To point 3, the issue was that the router itself was constantly complaining it couldn't resolve it's own hostname, because it was only looking for host, and not host.domain.tld. So I figured we should add both. Yes, that means EdgeOS needs to be fixed, but this was "faster". =)

 

I'll let the original author address the rest, but there are definitely some good points. My hope would be that Ubiquiti would actually use this script as a basis to make their local DNS based on leases actually work... 

USG, UniFi AP AC HD, UniFi US-16-150W, Unifi US-8, UCK-G2-Plus, 4x UVC-G3, UVC-G3-Flex
Regular Member
Posts: 590
Registered: ‎09-10-2012
Kudos: 380
Solutions: 35

Re: DNS resolution of local hosts


@staze wrote:

To point 3, the issue was that the router itself was constantly complaining it couldn't resolve it's own hostname, because it was only looking for host, and not host.domain.tld.


I just tested this before making my point 3 Man Happy It actually works well as long as:

- the router has itself as nameserver in /etc/resolv.conf (and it should have itself as only nameserver. dns forwarding should be done via dnsmasq forwarders)

- the router has the domain set in /etc/resolv.conf

 

if those two things are set, you can enter ping hostname and it works well, even if you only have hostname.domain.tld in your /etc/hosts

Regular Member
Posts: 501
Registered: ‎12-17-2015
Kudos: 200
Solutions: 10

Re: DNS resolution of local hosts

Interesting. I could not get it to work for the life of me. 

 

That said, and not to hijack, but how the heck do you set dnsmasq forwarding dns servers, and NOT have them be dns servers for the router? I actually had to tell the router to use google's servers because I was getting weird timeout issues... 

USG, UniFi AP AC HD, UniFi US-16-150W, Unifi US-8, UCK-G2-Plus, 4x UVC-G3, UVC-G3-Flex