I've been stuggling to get a redirection working via iptables - a combination of restrictive capabilities of the s/w versions on DD-WRT and (more likely) my shoddy script.

We have a very limited bandwidth out to the internet on the network in question, and so I've deployed a number of measures to make utilisation more efficient e.g. ad blocking & replacement technology, traffic filtering, QoS prioritisation

I have the ISP-supplied router as the outer router, and an inner router running WiFi, LANs & VPN out to the internet.

Specific Problem

Among the list of enhancements is DNS caching on a dedicated server node, and redirection of all DNS requests to this server. Most of this is done via network settings in the dhcp servers, but some mobile & desktop apps are overriding this.

I use DNS upstream servers as targets from the LAN DNS server which I know are trustworthy. The inner router has a firewall script which is designed to apply a vpn killswitch and allow sticky IP's access outside of the VPN / straight out via our ISP (thanks a bunch Netflix).

However I'm struggling to get the DNS traffic redirection working on DD-WRT - I tested it on a combination of servers and desktops using dig and tcpdump, simulating a request being made to server A, redirected to server B, and the client getting a DNS response accordingly. That appeared to work as expected, but when I applied the same configuration to the inner router it has no effect. I can't see any traffic arriving at the DNS server when I fire a request out to a public resolver e.g. Cloudflare.

In fact the request is still blocked.

lanDns="dns server IP"

iptables -I FORWARD -s ! $lanDns -p tcp --dport 53 -j ACCEPT
iptables -I FORWARD -s ! $lanDns -p udp --dport 53 -j ACCEPT
iptables -t nat -A PREROUTING -s ! $lanDns -p tcp --dport 53 -j DNAT --to $lanDns:53
iptables -t nat -A PREROUTING -s ! $lanDns -p udp --dport 53 -j DNAT --to $lanDns:53

iptables -I FORWARD -s ! $lanDns -p tcp --dport 5353 -j ACCEPT
iptables -I FORWARD -s ! $lanDns -p udp --dport 5353 -j ACCEPT
iptables -t nat -A PREROUTING -s ! $lanDns -p tcp --dport 5353 -j DNAT --to $lanDns:53
iptables -t nat -A PREROUTING -s ! $lanDns -p udp --dport 5353 -j DNAT --to $lanDns:53

# secure DNS - TODO letsencrypt certificate for <servername>
iptables -t nat -A PREROUTING -s ! $lanDns -p tcp --dport 853 -j DNAT --to $lanDns:853
# allow direct DNS traffic from the LAN DNS server, don't need to drop as well as this will only interfere
iptables -A FORWARD -p tcp --dport 53 -s $lanDns -j ACCEPT
iptables -A FORWARD -p udp --dport 53 -s $lanDns -j ACCEPT

iptables output looks ok, but what am I missing? Is it as simple as ordering or just my poor iptables usage? :)

target     prot opt source               destination         
DNAT       udp  --             udp dpt:53 to:[DNS server ip] 
DNAT       tcp  --             tcp dpt:53 to:[DNS server ip] 
DNAT       tcp  -- ![DNS server ip]           tcp dpt:53 to:[DNS server ip]:53 
DNAT       udp  -- ![DNS server ip]           udp dpt:53 to:[DNS server ip]:53 
DNAT       tcp  -- ![DNS server ip]           tcp dpt:5353 to:[DNS server ip]:53 
DNAT       udp  -- ![DNS server ip]           udp dpt:5353 to:[DNS server ip]:53 

iptables version on DD-WRT (Kong Ac): 1.3.7


Thanks to a comment by davidgo it appears that the requests are being redirected correctly, but the responses are getting as far as the router (but not being send back to the requestor). Will experiment with rule ordering a little more and update if progress - support welcome if that makes the problem easier to identify

Alternative Solutions Not Appropriate Here

I acknowledge that the following are good solutions, but are not appropriate in the context of this specific scenario:

  • PiHole - other nodes are delivering similar capabilities
  • Replacing router
  • Replacing DD-WRT firmware with a.n. other firmware distro
  • Non-Linux solutions
  • Closed-source solutions
  • Not sure its your problem but you either need to have the last 2 lines first in your script or you need to change the -A to a -I so the ordering is correct. – davidgo May 10 '19 at 20:12
  • 1
    I also posit that you may have an issue with the DNS server not responding back to the router, rather responding directly to the client which may not recognise the source address and thus drop the packet. I am guessing you might be able to do something messy with SNAT as well, bur I would put the router on a different subnet to the LAN so all traffic to and from it gies through the router. – davidgo May 10 '19 at 20:16
  • 2
    Lastly, tcpdump runs fine on dd-wrt. You may want to do some dumps to analyse the traffic flows and what is sent and received at various points. – davidgo May 10 '19 at 20:17
  • @davidgo (ref: ordering comment) thanks - but those last two lines allow the DNS server to make queries out over the VPN, and they appear to be working ok. Basic DNS service is all operational, however redirecting queries that ignore the DHCP servers domain-name-servers directive is not working i.e. they want to make direct DNS queries to whatever DNS server they have been built / configured to request from. – Rex May 11 '19 at 14:00
  • 1
    I suspect the source address may need to be rewritten as well as the target address. I postulate (ie JUST GUESSING) original packet has client-> extdns This is modified to client -> NewDNS so the server responds NewDNS to client. Because NewDNS is on the lan, it does not go back through router so the response is dropped by the client because it does recognise it as the response. – davidgo May 11 '19 at 19:29

Credit to davidgo for the hints in the right direction to solve this. What I hadn't spotted previously was that the requests were being redirected to the right end point, but the responses were not being passed back to the requester.

To solve that I used a postrouting command, and I'd also changed the order and alterered from append to inserts. The code excerpt below was also then used successfully to redirect tcp requests over 853 (sDNS), it's also part of a loop to reduce code duplication.

# declarations to make the excerpt make more sense    
lanDns="LAN server IP"
dnsPorts="53 5353"

for dnsPort in $dnsPorts; do
    iptables -t nat -I POSTROUTING -s ! $lanDns -p udp --dport $dnsPort -d $lanDns -j MASQUERADE

You don't necassarily want to try and redirect everything that could be DNS as often unusual failed DNS requests can be used to trace malware on your network. Having your own local DNS can really help spot unwelcome activity.


I found it useful to find my shoddy coding using || logger -p daemon.error "Error code $? for 4" at the end of each iptables command - replace 4 with some unique line indicator. DD-WRT makes it even harder in that these logged items don't get shunted through remote logging, so you can't watch them on your log server.

You still have to SSH onto the router and grep for the messages.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.