Tuesday, June 5, 2012

dnsmasq cache size tuning

dnsmasq is a lightweight DNS forwarder and DHCP server used primarily in embedded systems (like routers) where resources are limited. DNS queries are small in size but latency introduced can be significant depending on the proximity of a DNS server that can answer the query. DNS queries are usually cached either by a web browser of by the operating system but if several clients are connecting to the Internet through a router they would all need to send 'possibly' same DNS requests over again. That's why dnsmasq running on a router has a local cache for DNS queries.
To test if the dnsmasq caching is actually working locally on the router one could use nslookup:
flux@xubuntu:~$ time nslookup heise.de

Non-authoritative answer:
Name: heise.de

real 0m1.047s
user 0m0.008s
sys 0m0.012s

flux@xubuntu:~$ time nslookup heise.de

Non-authoritative answer:
Name: heise.de

real 0m0.035s
user 0m0.020s
sys 0m0.008s

The first query was not found in the dnsmasq's local cache and it took one second to get the response from a name server. The second query was found in the local cache and it only took 3.5 milliseconds to get the response!

By default dnsmasq has the cache size to keep 150 entries. If there is no space in the cache to store a new positive DNS response it will replace an older entry. Such replacements are logged with the dnsmasq's usage statistics. To get it logged one should call 'killall -s USR1 dnsmasq' and check the syslog:
Jun  2 17:30:43 daemon.info dnsmasq[1688]: time 1338651043
Jun  2 17:30:43 daemon.info dnsmasq[1688]: cache size 150, 126/5695 cache insertions re-used unexpired cache entries.
Jun  2 17:30:43 daemon.info dnsmasq[1688]: queries forwarded 1353, queries answered locally 1426

In this example 126 not expired DNS entries were replaced in a single browsing session. This is a good indication that the cache size is not optimal and it should be increased at least two times. It should be noted that dnmasq's cache is stored as a single chunk in the router's main memory which is usually limited. That's why the cache size should not be too big. On 32 bit platforms for every cache entry 82 bytes will be allocated if IPv6 is used and 74 bytes if IPv4 is used. On 64 bit platforms 94 and 86 respectively. The cache size is configured by the cache-size parameter in the dnsmasq.conf file.

Note for the openwrt firmware users: To change the dnsmasq's cache size it is necessary to add cachesize option into the dnsmasq section of the /etc/config/dhcp:
root@Buffalo:~# cat /etc/config/dhcp 

config dnsmasq
 option cachesize '500'

I'm setting cache-size to 500 and after restart of the dnsmasq I let it work for a couple of days before I check the result:

Jun  5 07:13:24 daemon.info dnsmasq[1605]: cache size 500, 0/6654 cache insertions re-used unexpired cache entries.
Jun  5 07:13:24 daemon.info dnsmasq[1605]: queries forwarded 2317, queries answered locally 8820
With this cache size I have zero reuse of unexpired cache entries.

It is also worth to note that to avoid flooding the dnsmasq's cache with entries I would never use I have disabled browser's dns prefetch functionality. By default browser scans for all links on each opened page and sends dns queries for unknown addresses so that when a link is clicked no time is spent for the name resolution. On the other hand dnsmask's cache is filled up with addresses I would never visit replacing useful ones. To deactivate prefetch functionality in the Firefox browser it is necessary to add a boolean preference named network.dns.disablePrefetch on the about:config page and set it to true. There is also a detailed description here.


Anonymous said...

Could explain how to "call 'killall -s USR1 dnsmasq' and check the syslog"? It must be in the router config, but I'm not familiar with it. I'm on a WRT54GL v1.1 with Tomato Shibby build 108. Thanks!

alex said...

telnet or ssh to your router and call this command in the shell.

Anonymous said...

Hi, you can simply SSH into your router and run the following commands;
# uci set dhcp.@dnsmasq[0].cachesize=5000

Then run;
# uci commit dhcp

Both ways work.
note: if router is rebooted all DNS cache is cleared/lost.
note: I set my cache to 5000 because I have 60 MB's of free ram on my router.

Anonymous said...

To view syslog entries execute the 'logread' command. There is no actual log file as it's stored by busybox in a RAM based ring buffer.

Anonymous said...

Thanks for the logread i was puzzled that it was nothing in /var/log/syslog

Anonymous said...

How much memory is expected to be used by dnsmasq if cache size is set to 500,000 it it 500,000x86 byte = 43MB ? . where can i find official docs regarding memory required per cache entry ?